• 【CF555E】Case of Computer Network(边双缩点水题)


    点此看题面

    • 给定一张(n)个点(m)条边的无向图以及(q)个点对((s,t))
    • 问是否存在一种给边定向的方案,使得每个(s)都能到达对应的(t)
    • (n,m,qle2 imes10^5)

    边双缩点+树上差分

    显然一个边双中一定存在一种定向方案使得任意两点都能互相到达。

    而若一条路径经过了两个边双中间的一条桥,那么这条桥的方向就被确定了。

    如果一条桥会被从两种方向经过,那么就产生了矛盾,说明无解。

    考虑边双缩点之后得到的是森林,那么每棵树中的两点(s,t)就是先从(s)向上走到(LCA)再向下走回(t),也就是(s)(LCA)的边都需要向上,(t)(LCA)的边都需要向下。

    那么只要树上差分一下,看看是否会有一条边被打上两种标记就好了。

    代码:(O(n+m+q))

    #include<bits/stdc++.h>
    #define Tp template<typename Ty>
    #define Ts template<typename Ty,typename... Ar>
    #define Reg register
    #define RI Reg int
    #define Con const
    #define CI Con int&
    #define I inline
    #define W while
    #define N 200000
    #define LN 20
    #define Gmin(x,y) (x>(y)&&(x=(y)))
    using namespace std;
    int n,m;
    namespace G
    {
    	int ee,lnk[N+5];struct edge {int to,nxt,p;}e[2*N+5];
    	I void add(CI x,CI y) {e[++ee].nxt=lnk[x],e[lnk[x]=ee].to=y;}
    	int d,dfn[N+5],low[N+5];I void Tarjan(CI x,CI lst=0)//边双
    	{
    		dfn[x]=low[x]=++d;for(RI i=lnk[x],t=1;i;i=e[i].nxt)
    		{
    			if(t&&e[i].to==lst) {t=0;continue;}
    			if(dfn[e[i].to]) {Gmin(low[x],dfn[e[i].to]);continue;}
    			if(Tarjan(e[i].to,x),Gmin(low[x],low[e[i].to]),low[e[i].to]<=dfn[x]) continue;
    			e[i].p=e[((i-1)^1)+1].p=1;
    		};
    	}
    	int cnt,c[N+5];I void Col(CI x,CI C,CI lst=0)//缩点
    	{
    		c[x]=C;for(RI i=lnk[x];i;i=e[i].nxt)
    			e[i].to^lst&&!e[i].p&&!c[e[i].to]&&(Col(e[i].to,C,x),0);
    	}
    }
    namespace T//缩点后得到的森林
    {
    	int ee,lnk[N+5];struct edge {int to,nxt;}e[2*N+5];
    	I void add(CI x,CI y) {e[++ee].nxt=lnk[x],e[lnk[x]=ee].to=y;}
    	int d[N+5],f[N+5][LN+5];I void Init(CI x)//初始化
    	{
    		RI i;for(i=1;i<=LN;++i) f[x][i]=f[f[x][i-1]][i-1];
    		for(i=lnk[x];i;i=e[i].nxt) e[i].to^f[x][0]&&
    			(d[e[i].to]=d[f[e[i].to][0]=x]+1,Init(e[i].to),0);
    	}
    	I int LCA(RI x,RI y)
    	{
    		RI i;for(d[x]<d[y]&&(x^=y^=x^=y),i=0;d[x]^d[y];++i) (d[x]^d[y])>>i&1&&(x=f[x][i]);
    		if(x==y) return x;for(i=LN;~i;--i) f[x][i]^f[y][i]&&(x=f[x][i],y=f[y][i]);return f[x][0];
    	}
    	int tg[N+5][2];I void U(CI x,CI y)//树上差分
    	{
    		RI z=LCA(x,y);z?(++tg[x][0],++tg[y][1],--tg[z][0],--tg[z][1]):(puts("No"),exit(0),0);//如果不连通直接无解
    	}
    	I void Check(CI x)//检验
    	{
    		for(RI i=lnk[x];i;i=e[i].nxt) e[i].to^f[x][0]&&
    			(Check(e[i].to),tg[x][0]+=tg[e[i].to][0],tg[x][1]+=tg[e[i].to][1]);
    		if(tg[x][0]&&tg[x][1]) puts("No"),exit(0);//如果一条边打上了两种标记就无解
    	}
    }
    int main()
    {
    	using namespace G;RI Qt,i,x,y;scanf("%d%d%d",&n,&m,&Qt);
    	for(i=1;i<=m;++i) scanf("%d%d",&x,&y),add(x,y),add(y,x);
    	for(i=1;i<=n;++i) !dfn[i]&&(Tarjan(i),0);for(i=1;i<=n;++i) !c[i]&&(Col(i,++cnt),0);//Tarjan+缩点
    	for(i=1;i<=ee;i+=2) e[i].p&&(T::add(c[e[i].to],c[e[i+1].to]),T::add(c[e[i+1].to],c[e[i].to]),0);//建新图
    	for(i=1;i<=cnt;++i) !T::d[i]&&(T::d[i]=1,T::Init(i),0);//初始化
    	W(Qt--) scanf("%d%d",&x,&y),T::U(c[x],c[y]);//将询问差分
    	for(i=1;i<=cnt;++i) T::d[i]==1&&(T::Check(i),0);return puts("Yes"),0;//检验
    }
    
  • 相关阅读:
    LaTeX不能识别eps文件?
    ubuntu 11.04系统清理(不断更新。。。)
    换Ubuntu邮件客户端Evolution为Thunderbird
    Byobu:打造多任务的Terminal
    Learning the Vi Editor, 6th Edition学习笔记(1)
    Ubuntu:让桌面显示回收站
    3rd Party Repository for Dropbox
    ubuntu 显示隐藏文件
    Ubuntu下的一款Dock工具AWN
    i686和x86_64的区别
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/CF555E.html
Copyright © 2020-2023  润新知