P7025 [NWRRC 2017] Grand Test | CF521E Cycling City 题解

CF521E Cycling City

P7025 [NWRRC 2017] Grand Test


CF521E Cycling City

思维好题。

首先发现,如果有三条路径,那么这些路径一定在一个双联通分量里,而且这个双联通分量不是一个环。这是做法一。

有更简洁的做法。

引理:无向图的 dfs 树上只存在树边和返祖边。

首先先把这个图的 dfs 树建出来。

发现这是个树上链覆盖状物问题。

先把边拍到儿子节点上。

考虑什么时候答案合法。发现当有一条链被覆盖两次时,一定会有答案。

考虑以下情况,其中 \(S\)\(T\) 为合法解。

手模一下,答案就清楚了:找到最长的被覆盖两次的原树上的链,那么两头分别为 \(S\)\(T\) 即可。注意 \(i\) 的父亲也被覆盖了两次。

首先,对非树边进行链覆盖。

若现在是第一次覆盖,那么在被覆盖的边上记录 这条非树边是哪一条边。

当有一条边被覆盖两次时,意味着现在有合法解了。显然这个解可以仅由现在枚举的这条非树边和以前记录的那一条覆盖了这条边的非树边得来。

那么这道题做法就显然了。

先重新用这两条非树边把原树覆盖一下。

枚举点 \(i \in [1,n]\),如果 \(i\) 所记录的边被覆盖两次,那么这个点就可能是 \(S\)\(T\)

取这些点中深度最大和深度最小的点作为 \(S\)\(T\) 即可。

然后树上 \(S\)\(T\) 的路径为第一组合法解。

经过第一条非树边为第二组合法解。

经过第二条非树边为第三组合法解。

考虑经过非树边的路径怎么输出。令 \(u\) 为这条非树边深度较小的端点。

如图,做法就显然了,分图中所示的三个步骤统计即可。

PS:若无解,每条链最多被覆盖一次,暴力跳 fa 暴力做链覆盖即可。

时间复杂度 \(O(m)\)

Code:

点击查看代码
#include<bits/stdc++.h>
#define int long long

using namespace std;

const int Size=(1<<20)+1;
char buf[Size],*p1=buf,*p2=buf;
char buffer[Size];
int op1=-1;
const int op2=Size-1;
#define getchar()                                                              \
(t == t && (t=(t=In)+fread(In, 1, 1 << 20, stdin), t == t)     \
	? EOF                                                                 \
	: *t++)
char In[1<<20],*t=In,*t=In;
inline int read()
{
	int x=0,c=getchar(),f=0;
	for(;c>'9'||c<'0';f=c=='-',c=getchar());
	for(;c>='0'&&c<='9';c=getchar())
		x=(x<<1)+(x<<3)+(c^48);
	return f?-x:x;
}
inline void write(int x)
{
	if(x<0) x=-x,putchar('-');
	if(x>9)  write(x/10);
	putchar(x%10+'0');
}

const int N=2e5+5;
int n,m,u[N],v[N],dep[N],f[N],id[N],cnt[N];
vector<int> G[N];
bool vis[N];

void dfs1(int p) // 建 dfs 树
{
    for(int to:G[p])
    {
        if(!vis[to])
        {
            vis[to]=1;
            f[to]=p;
            dep[to]=dep[p]+1;
            dfs1(to);
        }
    }
}

void up2(int u,int v) // 重新对两条非树边进行链覆盖
{
    while(u!=v)
    {
        if(dep[u]<dep[v]) swap(u,v);
        cnt[u]++;
        u=f[u];
    }
}

void output(int id1,int id2) // 输出答案
{
    up2(u[id1],v[id1]);
    up2(u[id2],v[id2]);
    int s=0,t=0;
    for(int i=1;i<=n;i++) // 找起点和终点
    if(cnt[i]==2)
    {
        if(s==0||dep[s]>dep[f[i]]) s=f[i]; // 因为边权化点权,所以深度最小点是 fa[i]
        if(t==0||dep[t]<dep[i]) t=i;
    }
    cout<<"YES\n";

    // 令 t 为深度较大点,t 为深度较小点

/*  ----------------------------------------------  */    
    
    // 第一条路径:s -> s
    cout<<dep[t]-dep[s]+1<<" "; 
    int nw=t;
    while(nw!=s)
    {
        cout<<nw<<" ";
        nw=f[nw];
    }
    cout<<nw<<"\n";
    
/*  ----------------------------------------------  */      
    
    // 第二条路径:t -> u1 -> v1 -> t
    // 注意:记录的路径是反的!

    vector<int> vec;

    nw=s;
    vec.push_back(nw);
    while(nw!=u[id1]) // s -> u1
    {
        nw=f[nw];
        vec.push_back(nw);
    }
    nw=v[id1]; // u1 -> v1
    vec.push_back(nw);
    while(nw!=t) // v1 -> t
    {
        nw=f[nw];
        vec.push_back(nw);
    }
    reverse(vec.begin(),vec.end()); // 翻转路径,即为所求

    cout<<vec.size()<<" ";
    for(int i:vec) cout<<i<<" ";
    cout<<"\n";

/*  ----------------------------------------------  */      
    // 第三条路径:t -> u2 -> v2 -> t
    // 注意:记录的路径是反的!

    vec.clear();
    nw=s;
    vec.push_back(nw);
    
    while(nw!=u[id2]) // s -> u2
    {
        nw=f[nw];
        vec.push_back(nw);
    }
    nw=v[id2]; // u2 -> v2
    vec.push_back(nw);
    while(nw!=t) // v2 -> t
    {
        nw=f[nw];
        vec.push_back(nw);
    }
    reverse(vec.begin(),vec.end()); // 翻转路径,即为所求
    
    cout<<vec.size()<<" ";
    for(int i:vec) cout<<i<<" ";
    cout<<"\n";
    exit(0); // 结束程序
}

void up(int u,int v,int nwid) //进行链覆盖
{
    while(u!=v)
    {
        if(dep[u]<dep[v]) swap(u,v);
        if(id[u]) { output(nwid,id[u]); exit(0); } // 被覆盖过意味着有答案,
        id[u]=nwid; // 记录被哪覆盖条边
        u=f[u];
    }
}

signed main()
{
	n=read();
    m=read();
    for(int i=1;i<=m;i++)
    {
        u[i]=read();
        v[i]=read();
        G[u[i]].push_back(v[i]);
        G[v[i]].push_back(u[i]);
    }
    for(int i=1;i<=n;i++)
    if(!vis[i]) vis[i]=1,dfs1(i);

    for(int i=1;i<=m;i++) if(dep[u[i]]>dep[v[i]]) swap(u[i],v[i]);
    // 将深度较浅的点放前面,减少码量
    for(int i=1;i<=m;i++)
    {
        if(f[v[i]]==u[i]) continue; // 跳过树边
        up(v[i],u[i],i); // 暴力覆盖非树边
    }
    cout<<"NO\n"; // 没有边被覆盖两次及以上,无解。
	return 0;
}

P7025 [NWRRC 2017] Grand Test

和上道题一样,但是多测。代码只需加个清空,改 exit(0) 即可。

Code:

点击查看代码

#include<bits/stdc++.h>
// #define int long long

using namespace std;

const int Size=(1<<20)+1;
char buf[Size],*p1=buf,*p2=buf;
char buffer[Size];
int op1=-1;
const int op2=Size-1;
#define getchar()                                                              \
(tt == t && (tt=(t=In)+fread(In, 1, 1 << 20, stdin), t == tt)     \
	? EOF                                                                 \
	: *t++)
char In[1<<20],*t=In,*tt=In;
inline int read()
{
	int x=0,c=getchar(),f=0;
	for(;c>'9'||c<'0';f=c=='-',c=getchar());
	for(;c>='0'&&c<='9';c=getchar())
		x=(x<<1)+(x<<3)+(c^48);
	return f?-x:x;
}
inline void write(int x)
{
	if(x<0) x=-x,putchar('-');
	if(x>9)  write(x/10);
	putchar(x%10+'0');
}

const int N=1e5+5;
int n,m;
int u[N],v[N];
vector<int> G[N];
int id[N];
int cnt[N];
bitset<N> vis;
int dep[N];
int f[N];

void dfs1(int p)
{
    for(int to:G[p])
    {
        if(!vis[to])
        {
            vis[to]=1;
            f[to]=p;
            dep[to]=dep[p]+1;
            dfs1(to);
        }
    }
}

void up2(int u,int v)
{
    while(u!=v)
    {
        if(dep[u]<dep[v]) swap(u,v);
        cnt[u]++;
        u=f[u];
    }
}
vector<int> vec;

void output(int id1,int id2)
{
    up2(u[id1],v[id1]);
    up2(u[id2],v[id2]);
    int t=0,s=0;
    for(int i=1;i<=n;i++)
    if(cnt[i]==2)
    {
        if(t==0||dep[t]>dep[f[i]]) t=f[i];
        if(s==0||dep[s]<dep[i]) s=i;
    }

    cout<<s<<" "<<t<<"\n";

/*  -----------------------------------------------------------------------------------------  */    

    cout<<dep[s]-dep[t]+1<<" ";
    int nw=s;
    vis=0;

    while(nw!=t)
    {
        cout<<nw<<" ";
        nw=f[nw];
    }
    cout<<nw<<"\n";

/*  -----------------------------------------------------------------------------------------  */    

    vec.clear();
    nw=t;
    vec.push_back(nw);
    while(nw!=u[id1])
    {
        nw=f[nw];
        vec.push_back(nw);
    }

    nw=v[id1];
    
    vec.push_back(nw);
    while(nw!=s)
    {
        nw=f[nw];
        vec.push_back(nw);
    }
    reverse(vec.begin(),vec.end());

    cout<<vec.size()<<" ";
    for(int i:vec) cout<<i<<" ";
    cout<<"\n";

/*  -----------------------------------------------------------------------------------------  */    

    vec.clear();

    nw=t;
    vec.push_back(nw);
    
    while(nw!=u[id2])
    {
        nw=f[nw];
        vec.push_back(nw);
    }
    nw=v[id2];
    vec.push_back(nw);
    while(nw!=s)
    {
        nw=f[nw];
        vec.push_back(nw);
    }
    reverse(vec.begin(),vec.end());
    
    cout<<vec.size()<<" ";
    for(int i:vec) cout<<i<<" ";
    cout<<"\n";
}

bool up(int u,int v,int nwid)
{
    while(u!=v)
    {
        if(dep[u]<dep[v]) swap(u,v);
        if(id[u]) { output(nwid,id[u]); return 1; } 
        id[u]=nwid;
        u=f[u];
    }
    return 0;
}

void clear()
{
    for(int i=1;i<=n;i++)
    {
        G[i].clear();
        id[i]=0;
        cnt[i]=0;
        dep[i]=0;
        f[i]=0;
    }
    vis=0;
}
void solve()
{
    clear();
	n=read();
    m=read();
    for(int i=1;i<=m;i++)
    {
        u[i]=read();
        v[i]=read();
        G[u[i]].push_back(v[i]);
        G[v[i]].push_back(u[i]);
    }
    int root=n+1;
    vis[root]=1;
    for(int i=1;i<=n;i++)
    if(!vis[i]) vis[i]=1,dfs1(i);

    for(int i=1;i<=m;i++) if(dep[u[i]]>dep[v[i]]) swap(u[i],v[i]);
    for(int i=1;i<=m;i++)
    {
        if(f[v[i]]==u[i]) continue;
        if(up(v[i],u[i],i)) return;
    }
    cout<<"-1\n";
}

signed main()
{
    int T=read();
    while(T--) solve();
}

posted @ 2025-09-08 22:09  Wy_x  阅读(9)  评论(0)    收藏  举报