P8426 [JOI Open 2022] 放学路(School Road)
题目链接
- 给定无向图,判断是否有
1\to n 且长度不等于最短路的简单路径。 -
根据部分分大力得到正解,一步步来。
首先对于
不难条件反射出广义串并联图方法(广告:广义串并联图方法学习笔记),缩合后得到
首先先描述一下具体的缩合方法:
- 一度点就直接删,二度点的边权就直接相加。
- 叠合重边则需要注意:
- 如果重边的边权不同则将边权设置为不可到达,比如
-1 (因为路径上显然不能包含这两个重边)。 - 否则就还是原权值。
- 如果重边的边权不同则将边权设置为不可到达,比如
- 同时还有特殊的一点,就是
1,n 即使度数\leq 2 也不作为缩合对象。
自行想象一下 no 的情况,大概就是所有
于是大胆断言答案为 no 当且仅当:缩合后
当然这个猜测随便 hack,比如 Froggy 在 LOJ 讨论 里提到的 hack 长这样:
虽然这个 hack 看上去有些取巧,但是至少是个 hack,而且变相的更加使我们相信这个不靠谱的猜想有一定正确性。
再看一眼部分分,发现有:对于任意三座不同的城市 a, b, c,均存在一条从城市
翻译成人话就是整个图形成一个点双,同时不难发现上述 hack 的思路就是存在不同时包含
那么难道说如果整张图是一个点双上述猜想就正确吗,实际上是这样的,证明如下:
此处证明参考官方题解并结合自己的一点思考,可以看作是本题最核心部分之一。
首先整理一下已知条件:整个图是点双联通的且所有点的度数
考虑将所有边定向,根据从
如果此时就有
否则定向后,称入度
然后考虑
首先得到
同时由于任意点度数
考虑找到这样一条边,使得
根据定义,一定存在
除了
如果它不是简单路径,不难发现只有可能是
那么将原路径替换为
所以这条路径一定是简单的,且
如此,所有非正解的部分分都有了解决方法。
当然更进一步也是容易的,因为如果从
所以直接加一条
复杂度
#include<bits/stdc++.h>
typedef long long ll;
#define rep(i, a, b) for(int i = (a); i <= (b); i ++)
#define per(i, a, b) for(int i = (a); i >= (b); i --)
#define Ede(i, u) for(int i = head[u]; i; i = e[i].nxt)
using namespace std;
#define eb emplace_back
typedef pair<int, ll> pii;
#define mp make_pair
#define fi first
#define se second
inline int read() {
int x = 0, f = 1; char c = getchar();
while(c < '0' || c > '9') f = (c == '-') ? - 1 : 1, c = getchar();
while(c >= '0' && c <= '9') x = x * 10 + c - 48, c = getchar();
return x * f;
}
const int N = 1e5 + 10;
const ll inf = 1e16;
int n, m;
unordered_map<int, ll> g[N];
void add(int u, int v, ll w) {
if(g[u].find(v) != g[u].end()) {
if(g[u][v] != w) g[u][v] = g[v][u] = -1;
}
else g[u][v] = g[v][u] = w;
}
ll dis[N]; bool vis[N];
vector<pii> h[N];
ll getdis() {
priority_queue<pair<ll, int> > q;
rep(i, 1, n) dis[i] = inf;
dis[1] = 0, q.push(mp(0, 1));
while(! q.empty()) {
int u = q.top().se; q.pop();
if(vis[u]) continue;
vis[u] = true;
for(auto e : h[u]) {
int v = e.fi; ll w = e.se;
if(dis[v] > dis[u] + w)
dis[v] = dis[u] + w, q.push(mp(- dis[v], v));
}
}
return dis[n];
}
int dfn[N], low[N], stk[N], top, tim, cnt;
vector<int> scc[N];
void dfs(int u) {
dfn[u] = low[u] = ++ tim;
stk[++ top] = u;
for(auto e : h[u]) {
int v = e.fi;
if(! dfn[v]) {
dfs(v), low[u] = min(low[u], low[v]);
if(low[v] == dfn[u]) {
int o = 0; cnt ++;
do {o = stk[top --], scc[cnt].eb(o);} while(o != v);
scc[cnt].eb(u);
}
}
else low[u] = min(low[u], dfn[v]);
}
}
bool valid[N];
void build() {
n = read(), m = read();
rep(i, 1, m) {
int u = read(), v = read(), w = read();
h[u].eb(mp(v, w));
h[v].eb(mp(u, w));
}
ll cur = getdis();
h[1].eb(mp(n, cur));
h[n].eb(mp(1, cur));
dfs(1);
rep(i, 1, n) vis[i] = false;
int pos = 0;
rep(i, 1, cnt) {
for(int o : scc[i]) vis[o] = true;
if(vis[1] && vis[n]) {pos = i; break;}
for(int o : scc[i]) vis[o] = false;
}
assert(pos);
for(int o : scc[pos]) valid[o] = true;
rep(u, 1, n) if(valid[u])
for(auto e : h[u]) if(valid[e.fi]) add(u, e.fi, e.se);
}
queue<int> q;
void push(int u) {if(u != 1 && u != n && (int) g[u].size() <= 2) q.push(u);}
int main() {
build();
rep(i, 1, n) push(i);
while(! q.empty()) {
int u = q.front(); q.pop();
if(g[u].empty()) continue;
for(auto o : g[u]) g[o.fi].erase(u);
if((int) g[u].size() == 2) {
auto cur = g[u].begin();
int x = cur -> fi; ll a = cur -> se; cur ++;
int y = cur -> fi; ll b = cur -> se;
ll w = (a == -1 || b == -1) ? -1 : a + b;
add(x, y, w);
}
for(auto o : g[u]) push(o.fi);
g[u].clear();
}
if(g[1].find(n) != g[1].end() && g[1][n] != -1 && (int) g[1].size() == 1) puts("0"); else puts("1");
return 0;
}