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;//回溯了 
}

思路和图片来自这里,我整理了代码的注释部分,如有问题请联系作者

posted @ 2020-12-02 11:47  星&夜  阅读(46)  评论(0编辑  收藏  举报