CF521E
CF521E
给定一张 n 个点 m 条边的无向简单图。
问图中能否找到两个点,满足这两个点之间有至少三条完全不相交的简单路径。
n,m ≤ 2 × 1e5,图不保证连通。
=======================================================
两点间有三条完全不相交路径,可将问题转化为
两个环有边相交,再转换一下
一棵树上的一条树边被两条非树边覆盖
即,在下面的图中,树边 d -> lca 被两条非树边覆盖,这样就找到了三条路径
1、树上的那条路径,即 d -> lca
2、d -> b -> a -> p
3、d -> c -> p
code
int v[N],ins[N];
int cx[N],cy[N];
//cx,cy表示被那条边覆盖了
//下面的程序中,cy 是较浅的那个
int lca(int x,int y)//如此暴力的 lca 我还是第一次写
{
while(dep[x] > dep[y]) x = fa[x];
while(dep[x] < dep[y]) y = fa[y];
while(x != y) x = fa[x],y = fa[y];
return x;
}
int tmp[N];
int tp;
//来记录答案的路径
void add_path(int x,int y)
{
while(x != y)
{
tmp[++ tp] = x;
x = fa[x];
}
tmp[++ tp] = y;
}
void ccout()
{
cout << tp << " ";
for(int i = 1;i <= tp;i ++)
cout << tmp[i] << ' ';
cout << endl;
tp = 0;//将路径清空,准备下一次记录
}
void get(int a,int b,int c,int d)
{
if(dep[b] > dep[d]) swap(a,c),swap(b,d);
//现在的话,就是,ab深度较小,cd较深
int p = lca(a,c);
puts("YES");
add_path(p,d);//1、树上的那条路径
reverse(tmp + 1,tmp + 1 + tp);
//将路径顺序颠倒,从浅的开始输出
ccout();
add_path(d,b);//2、d -> b -> a -> p 的这条路径
add_path(a,p);
ccout();
tmp[++ tp] = d;//3、 d -> c -> p 的那条路径
add_path(c,p);
ccout();
exit(0);//结束程序
}
void dfs(int x)
{
v[x] = 1;//当前点已被遍历
ins[x] = 1;//标记当前点,在回溯的时候取消标记
for(int i = head[x];i;i = nxt[i])
{
int y = to[i];
if(y == fa[x]) continue;
if(!v[y])
{
dep[y] = dep[x] + 1;
fa[y] = x;
dfs(y);
}
else if(ins[y])//我们这时候发现有一个被标记的点,也就是我们发现了一个环
for(int u = x;u != y;u = fa[u])//注意 :现在的 y 才是深度较浅的那个
if(cx[u] && cy[u]) get(cx[u],cy[u],x,y);//既然已经被覆盖过了,当前这次就是第二次覆盖了,可以结束了
else cx[u] = x,cy[u] = y;//没有被覆盖过 ,标记一下这个环被 x ,y 覆盖
}
ins[x] = 0;//回溯了
}
思路和图片来自这里,我整理了代码的注释部分,如有问题请联系作者