CF555E Case of Computer Network题解

该博客探讨了一种无向图定向的方法,以确保在定向后的图中,所有指定的询问都能从起点x到达终点y。作者证明了在没有桥的边双连通分量中,存在定向方案使所有点两两可达,并通过边双缩点、LCA(最近公共祖先)计算和树上差分来实现这一目标。文章适合对图论算法感兴趣的读者。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意:给你一个无向图和一些询问(x,y),要求你把无向图里的边定向后所有询问的x都能走到y,可以输出Yes,否则输出No
省选组作业里最简单的一道题 (By SLS)
定理:在同一个边双连通分量中,存在方案使得它的边定向后点两两可达
因为没有桥,边双中肯定有环,定理易证
想到这个后对图进行边双缩点
对一个询问(x,y),设X=x所在边双编号,Y=y所在边双编号
X=Y,不用管(由定理)
如果X,Y不在同一棵树里,必定是No
剩下的情况,X-LCA的边方向必须向上,LCA-Y的边方向必须向下
开两个数组后,树上差分就OK

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <map>
using namespace std;
int bridge[400005],f[400005][3],q[200005],vis[200005],dep[200005],son[200005];
struct node{
	int x,y;
}a[200005];
map<int,int> h[200005];
int i,j,k,m,n,o,p,l,s,t,tot,x,y,bz,times,conum;
int low[200005],dfn[200005],co[200005],b[200005][18],mi[18],up[200005],down[200005];
void insert(int x,int y) {f[++t][1]=y,f[t][2]=q[x],q[x]=t;}
void Tarjan(int t,int edge)
{
	dfn[t]=low[t]=++tot;
	for (int k=q[t];k;k=f[k][2])
	{
		if (!dfn[f[k][1]])
		{
			Tarjan(f[k][1],k);
			low[t]=min(low[t],low[f[k][1]]);
			if (dfn[t]<low[f[k][1]]) bridge[k]=bridge[k^1]=1;
		} else if (k!=(edge^1)) low[t]=min(low[t],dfn[f[k][1]]);
	}
}
void dg(int t)
{
	co[t]=tot;
	for (int k=q[t];k;k=f[k][2])
	{
		if (co[f[k][1]]||bridge[k]) continue;
		dg(f[k][1]);
	}
}
void dfs(int t,int fa)
{
	b[t][0]=fa,vis[t]=tot,son[fa]=t,dep[t]=dep[fa]+1; 
	for (int i=1;i<=17;i++) b[t][i]=b[b[t][i-1]][i-1];
	for (int k=q[t];k;k=f[k][2])
	{
		if (vis[f[k][1]]) continue;
		dfs(f[k][1],t);
	}
}
void getsum(int t)
{
	vis[t]=1;
	for (int k=q[t];k;k=f[k][2])
	{ 
		if (!vis[f[k][1]]) {
			if (!bz) return;
			getsum(f[k][1]);
			up[t]+=up[f[k][1]],down[t]+=down[f[k][1]];
			if (!bz) return;
		}
		if (!bz) return;
	}
	if (up[t]>0&&down[t]>0) {
		bz=0;return;
	}
	if (!bz) return;
}
int getlca(int x,int y)
{
	if (dep[x]<dep[y]) swap(x,y);
	int k=dep[x]-dep[y];
	for (int i=17;i>=0;i--)
		if (k>=mi[i]) k-=mi[i],x=b[x][i];
	if (x==y) return x;
	for (int i=17;i>=0;i--)
		if (b[x][i]!=b[y][i]) x=b[x][i],y=b[y][i];
	return b[x][0];
}
void read(int &x)
{
	char ch=getchar();x=0;
	while (ch<'0'||ch>'9') ch=getchar();
	while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-48,ch=getchar();
}
int main()
{
	freopen("network.in","r",stdin);
	freopen("network.out","w",stdout);
	read(n),read(m),read(times);t=1;
	for (mi[0]=i=1;i<=17;i++) mi[i]=mi[i-1]*2;
	for (i=1;i<=m;i++) read(a[i].x),read(a[i].y),insert(a[i].x,a[i].y),insert(a[i].y,a[i].x);
	for (i=1;i<=n;i++) 	
		if (!dfn[i]) Tarjan(i,0);
	tot=0;
	for (i=1;i<=n;i++)
		if (!co[i]) co[i]=++tot,dg(i);
	memset(f,0,sizeof(f)),memset(q,0,sizeof(q)),t=1,conum=tot;
	for (i=1;i<=m;i++) insert(co[a[i].x],co[a[i].y]),insert(co[a[i].y],co[a[i].x]);
	tot=0;memset(vis,0,sizeof(vis));
	for (i=1;i<=n;i++)
		if (!vis[i]) tot++,dfs(i,0);
	while (times--)
	{
		read(x),read(y);x=co[x],y=co[y];
		if (h[x][y]) continue;h[x][y]=1;
		if (x==y) continue;
		if (vis[x]!=vis[y]) {
			puts("No");return 0;
		} 
		int lca=getlca(x,y);
		up[lca]--,up[x]++,down[lca]--,down[y]++;
	}
	memset(vis,0,sizeof(vis));
	bz=1;
	for (i=1;i<=conum;i++)
		if (!vis[i]) getsum(i);
	wyd:puts(bz?"Yes":"No");
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值