P r o b l e m \mathrm{Problem} Problem
幻象群岛是由 n n n个孤立的岛屿构成。岛屿之间有一些残破的石桥,而桥心的石墩上,就有可能镶嵌着上古魔法石。约翰尼可以通过这些石桥,从一座岛跑到另一座岛,如果岛上恰好有魔法石,他就可以顺便收集。但是由于这些石桥实在是太残破了,约翰尼经过之后,石桥就会崩塌,不能再次通过。(由于约翰尼踩过的部分很快就会崩塌,所以他也不能先跑到桥心,然后原路返回)。
约翰尼现在处在岛a,而岛b上则有一个传送门,只有在那里,约翰尼才能安全地离开幻象群岛。约翰尼想知道,他能顺利地收集到至少一块上古魔法石,并安全离开吗?
S o l u t i o n \mathrm{Solution} Solution
前置芝士:
- 边双连通分量:该分量内任意两点都存在两条边不想交的路径。
- 点双连通分量:该分量内任意两点都存在两条点不相交的路径。
我们知道边双的求法可以直接使用割边来分割,即如果一条边是割边,那么它一定不属于任意一个边双内;而且,一个边双内也不存在割边。
回归本题:
联系变双,求
s
s
s到
t
t
t是否存在一条路径,使得一条边的边权为
1
1
1,可以转化为:
- 添加边 ( s , t , 0 ) (s,t,0) (s,t,0)的情况下是否存在两条从 s s s到 t t t的两条不相交的路径。
- 相当于问你 s s s和 t t t是否位于一个边双内而且边双内是否存在边权为1的边。
最后我们只需要用并查集判断即可
#include <bits/stdc++.h>
using namespace std;
const int N = 3e5 + 10;
int n, m, cnt = 0, tot = 1;
int Link[N], low[N], dfn[N], Bri[N], fa[N], R[N], vis[N];
struct node {
int x, y, next, v;
} e[N * 2];
void Clear(void)
{
memset(R,0,sizeof R);
memset(vis,0,sizeof vis);
memset(low,0,sizeof low);
memset(dfn,0,sizeof dfn);
memset(Bri,0,sizeof Bri);
memset(Link,0,sizeof Link);
cnt = 0; tot = 1;
return;
}
int read(void)
{
int s = 0, w = 0; char c = getchar();
while (c < '0' || c > '9') w |= c == '-', c = getchar();
while (c >= '0' && c <= '9') s = s*10+c-48, c = getchar();
return w ? -s : s;
}
int get(int x) {
if (fa[x] == x) return x;
return fa[x] = get(fa[x]);
}
void tarjan(int x,int last)
{
dfn[x] = low[x] = ++ cnt;
for (int i=Link[x];i;i=e[i].next)
{
int y = e[i].y, v = e[i].v;
if (dfn[y] == 0) {
tarjan(y,i);
low[x] = min(low[x],low[y]);
if (dfn[x] < low[y]) Bri[i] = Bri[i^1] = 1;
}
else if (i != (last ^ 1))
low[x] = min(low[x],dfn[y]);
}
return;
}
void add(int x,int y,int v) {
e[++tot] = {x,y,Link[x],v};
Link[x] = tot;
}
void work(void)
{
n = read(), m = read();
for (int i=1;i<=m;++i)
{
int x = read(), y = read(), v = read();
add(x,y,v), add(y,x,v);
}
int S = read(), T = read();
add(S,T,0);
add(T,S,0);
for (int i=1;i<=n;++i)
if (dfn[i] == 0) tarjan(i,0);
for (int i=1;i<=n;++i) fa[i] = i;
for (int i=2;i<=tot;i+=2)
if (Bri[i] == 0) {
int x = e[i].x;
int y = e[i].y;
int v = e[i].v;
fa[get(x)] = get(y);
if (v == 1) vis[x] = vis[y] = 1;
}
for (int i=1;i<=n;++i) R[get(i)] |= vis[i];
if (get(S) == get(T) and R[get(S)]) puts("YES");
else puts("NO");
Clear();
return;
}
int main(void)
{
int T = read();
while (T --) work();
return 0;
}