Codeforces 555E

100 篇文章 0 订阅
16 篇文章 0 订阅

比较简单的题。
考虑一个边双,我们可以巧妙给边定向让它变成一个强连通分量。做法是搞个dfs树,树边从父亲往儿子,非树边从儿子往父亲,容易证明这样一定合法。
于是我们可以把边双缩起来得到一棵树,问题就变成给树边定向,这个随便判一下就好了。

#include <bits/stdc++.h>
#define FR first
#define SE second
 
using namespace std;
 
typedef pair<int,int> pr;
 
struct Edge {
  int t,next;
  Edge() {}
  Edge(int a,int b):t(a),next(b) {}
};
 
Edge e[400005];
int head[200005];
bool vis[200005];
 
int dfn[200005],low[200005],dfs_cnt;
 
void dfs1(int x,int fa) {
  dfn[x]=low[x]=++dfs_cnt;
  for(int i=head[x];i;i=e[i].next)
    if (((i+1)>>1)!=fa) {
    	int u=e[i].t;
    	if (!dfn[u]) {
    		dfs1(u,(i+1)>>1);
    		low[x]=min(low[x],low[u]);
    		if (low[u]>dfn[x]) vis[(i+1)>>1]=1;
		}
		else low[x]=min(low[x],dfn[u]);
	}
}
 
int id[200005],bcc_cnt;
vector <int> ee[200005];
 
void dfs2(int x) {
  id[x]=bcc_cnt;
  for(int i=head[x];i;i=e[i].next)
    if (!id[e[i].t]&&!vis[(i+1)>>1]) dfs2(e[i].t);
}
 
int fa[200005][20],dep[200005],bel[200005],cnt;
int sum1[200005],sum2[200005];
 
void dfs3(int x) {
  bel[x]=cnt;
  for(int i=0;i<ee[x].size();i++)
    if (ee[x][i]!=fa[x][0]) {
    	int u=ee[x][i];
    	fa[u][0]=x;dep[u]=dep[x]+1;
    	for(int j=1;j<20;j++) fa[u][j]=fa[fa[u][j-1]][j-1];
    	dfs3(u);
	}
}
 
int lca(int x,int y) {
  if (dep[x]<dep[y]) swap(x,y);
  int d=dep[x]-dep[y];
  for(int i=0;i<20;i++)
    if ((d>>i)&1) x=fa[x][i];
  if (x==y) return x;
  for(int i=19;i>=0;i--)
    if (fa[x][i]!=fa[y][i]) {
    	x=fa[x][i];
    	y=fa[y][i];
	}
  return fa[x][0];
}
 
void dfs4(int x) {
  for(int i=0;i<ee[x].size();i++)
    if (ee[x][i]!=fa[x][0]) {
    	int u=ee[x][i];
    	dfs4(u);
    	sum1[x]+=sum1[u];
    	sum2[x]+=sum2[u];
	}
}
 
int main() {
  int n,m,k;
  scanf("%d%d%d",&n,&m,&k);
  for(int i=1;i<=m;i++) {
  	int x,y;
  	scanf("%d%d",&x,&y);
  	e[2*i-1]=Edge(y,head[x]);
  	head[x]=2*i-1;
  	e[2*i]=Edge(x,head[y]);
  	head[y]=2*i;
  }
  for(int i=1;i<=n;i++)
    if (!dfn[i]) dfs1(i,0);
  for(int i=1;i<=n;i++)
    if (!id[i]) {
    	bcc_cnt++;
    	dfs2(i);
	}
  for(int i=1;i<=n;i++)
    for(int j=head[i];j;j=e[j].next)
      if (id[i]!=id[e[j].t]) ee[id[i]].push_back(id[e[j].t]);
  for(int i=1;i<=bcc_cnt;i++)
    if (!bel[i]) {
    	cnt++;
    	dfs3(i);
	}
  for(int i=1;i<=k;i++) {
  	int x,y;
  	scanf("%d%d",&x,&y);
  	x=id[x];y=id[y];
  	if (bel[x]!=bel[y]) {
  		puts("No");
  		return 0;
	  }
  	int p=lca(x,y);
  	sum1[x]++;sum1[p]--;
  	sum2[y]++;sum2[p]--;
  }
  for(int i=1;i<=bcc_cnt;i++)
    if (!fa[i][0]) dfs4(i);
  for(int i=1;i<=bcc_cnt;i++)
    if (sum1[i]&&sum2[i]) {
    	puts("No");
    	return 0;
	}
  puts("Yes");
  return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值