CF521E Cycling City 解题报告

发布时间 2023-06-17 10:40:20作者: luo_shen

题面

一道难得恰到好处的构造题。

分析

因为要构造三条从 \(s\)\(t\) 的路径,且三条路径中任意两条路径经过的点集的交集等于 \(\{s,t\}\)。我们知道当两条路径经过的点集的交集等于 \(\{s,t\}\) 时,这两条路径将会构成一个环。因此题意转化为要求我们找到两个经过的边集有重合的环。看似这步转化是将题目变复杂了,其实还是变简单了。将图画出来,大致长成下图的样子。image

两个环分别是 \(s\rightarrow a\rightarrow t\)\(s\rightarrow b\rightarrow c\rightarrow t\)。可以发现,如果建出 dfs 树,那么所有的非树边都将是返祖边,那么我们只需要统计每条边被哪条边所覆盖,当其被第二次覆盖时,这两条边就是我们要找的 \((a,t)\)\((b,c)\) 了。可以发现,找边的覆盖并不需要用到非树边为返祖边的性质,所以任意生成一棵生成树均可实现以上的操作。;

这里再介绍另一种做法。对于找环这个事情,我们首先想到的是 tarjan 找点双。那么同时找两个边集有交环同理也是可以用 tarjan 找的。回到上图,我们要找的其实是 \((a,t)\)\((b,c)\) 两条边。令 \(low_u\) 表示以 \(u\) 为起点,经过至多一条返祖边能到达的最小 dfs 序,在哪个点走的这条返祖边显然也是可以在 tarjan 中得到的。令 \(nxt_u\) 表示以 \(u\) 为起点,经过至多一条返祖边能到达的次小 dfs 序(不必严格次小),也能找到从哪个点走的返祖边。可以发现从起点 \(s\) 开始,经过一条返祖边,到达的 \(t\) 点和 \(c\) 点的 dfs 序均小于 \(s\)。于是我们找到了有解的情况,存在一个点 \(s\) 使得 \(low_s<dfn_s\)\(nxt_s<dfn_s\)。这两个地方都不带等号,因为如果带了等号就会出现两个环上的点有交集,边却没有交集的情况,这是不符合要求的。

可以发现上面的分析中,至多一条是被标粗的。强调至多一条,一是 tarjan 本身是这么要求的,还有就是如果不要求至多一条,就会出现下图环套环的情况。image

这种时候,如果不要求经过至多一条返祖边,则会出现终点选到 \(c\)的情况,因为 \(t\) 不能选两次,导致只会有两条合法的路径。实际上我们要求经过至多一条返祖边,终点会选到 \(t\),这样就有三条合法的路径。

Code

这里给出第二种用 tarjan 的做法的代码。

点击查看代码
#include<bits/stdc++.h>
#define ull unsigned long long
#define ll long long
#define pii pair<int,int>
#define pdi pair<double,int>
#define pb push_back
#define eps 1e-9
#define mp make_pair
using namespace std;
namespace IO{
    template<typename T>
    inline void read(T &x){
        x=0;
        int f=1;
        char ch=getchar();
        while(ch>'9'||ch<'0'){
            if(ch=='-'){
                f=-1;
    }
            ch=getchar();
        }
        while(ch>='0'&&ch<='9'){
            x=x*10+(ch-'0');
            ch=getchar();
        }
        x=(f==1?x:-x);
    }
    template<typename T>
    inline void write(T x){
        if(x<0){
            putchar('-');
            x=-x;
        }
        if(x>=10){
            write(x/10);
        }
        putchar(x%10+'0');
    }
    template<typename T>
    inline void write_endl(T x){
        write(x);
        putchar('\n');
    }
    template<typename T>
    inline void write_space(T x){
        write(x);
        putchar(' ');
    }
}
using namespace IO;
const int N=2e5+10,inf=1e9;
int n,m,dfn[N],rdfn[N],fa[N],idx;
pii low[N],nxt[N];
vector<int>e[N];
deque<int>ans;
void tarjan(int u,int father){
    fa[u]=father;
    dfn[u]=++idx;
    rdfn[idx]=u;
    low[u]=mp(idx,u),nxt[u]=mp(inf,0);
    for(auto v:e[u]){
        if(!dfn[v]){
            tarjan(v,u);
            if(low[v]<low[u]){
                swap(low[u],low[v]);
            }
            if(low[v]<nxt[u]){
                swap(nxt[u],low[v]);
            }
        }
        else if(v!=fa[u]){
            pii tmp=mp(dfn[v],u);
            if(tmp<low[u]){
                swap(low[u],tmp);
            }
            if(tmp<nxt[u]){
                swap(nxt[u],tmp);
            }
        }
    }
    if(low[u].first<dfn[u]&&nxt[u].first<dfn[u]){
        puts("YES");
        int ed=rdfn[nxt[u].first],now=u;
        while(now!=ed){
            ans.pb(now);
            now=fa[now];
        }
        ans.pb(ed);
        write_space(ans.size());
        while(ans.size()){
            write_space(ans.front());
            ans.pop_front();
        }
        putchar('\n');
        now=nxt[u].second;
        while(now!=u){
            ans.push_front(now);
            now=fa[now];
        }
        ans.push_front(u);
        ans.pb(ed);
        write_space(ans.size());
        while(ans.size()){
            write_space(ans.front());
            ans.pop_front();
        }
        putchar('\n');
        now=ed;
        while(now!=rdfn[low[u].first]){
            ans.push_front(now);
            now=fa[now];
        }
        ans.push_front(rdfn[low[u].first]);
        now=low[u].second;
        while(now!=u){
            ans.push_front(now);
            now=fa[now];
        }
        ans.push_front(u);
        write_space(ans.size());
        while(ans.size()){
            write_space(ans.front());
            ans.pop_front();
        }
        exit(0);
    }
}
void solve(){
    read(n),read(m);
    for(int i=1;i<=m;i++){
        int u,v;
        read(u),read(v);
        e[u].pb(v);
        e[v].pb(u);
    }
    for(int i=1;i<=n;i++){
        if(!dfn[i]){
            tarjan(i,0);
        }
    }
    puts("NO");
}
signed main(){
    #ifndef ONLINE_JUDGE
        freopen("1.in","r",stdin);
        freopen("1.out","w",stdout);
    #endif
    int t=1;
    while(t--){
        solve();
    }
    return 0;
}