arc092 F Two Faced Edges

         题意:给你一个n个点m条边的有向图,问你每条边反向之后联通分量的个数会不会改变。

         吐槽一下,感觉atcoder就两种题,一种神奇的计数题,一种题就在问你把第i个啥换掉之后的答案有什么变化,还有就是评测机飞快,比如下面的题正解复杂度就是 2000×200000。

         设改变的边 为 u -> v,那么 下列两个条件有且仅有一个成立的时候,答案数量会改变。

         1.   v->u 本身是否联通

         2.   去掉u->v这条边之后,u->v 可以通过其他方式联通。

         第一个我们可以开始n*m直接暴力做,一个做第二个的时候顺便判。

         我们假设和x 相连的点为 y1 ,y2, y3, yk。如果我们能记录从x出发到 yi 的所有路径里其中的第一条和最后一条,如果它们都必须经过xi->yi这条边的话,就说明了这条边是一定会被选的。那么我们对于每个点 x, 就从1到k 逐个dfs 它所连接的每一个yi,  要求不过x,如果 x->yj 在加其他边的时候已经联通了,说明x->yj这条边可以不选,接着我们再倒着从k到1 逐个dfs它所连接的每一个yi,如果 x->yj在其他边的时候已经联通,同样说明这条边可以不选。 如果两次dfs发现,都是在假如x->yj的边的时候x-yj才联通,那么说明x-yj必须选了。 在dfs中我们用bel[a][b][0/1]表示从第a个点出发,正着/倒着枚举的时候使得与b联通的第一条边即可。

        下附AC代码。

#include<iostream>
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<vector>
#define maxn 1005
#define _ 0
using namespace std;
struct nod
{
	int nex,id;
	nod(int a,int b)
	{
		nex=a;
		id=b;
	}
	nod(){}
};
int n,m,tim;
int vis[maxn];
int bel[maxn][maxn][2];
int u[maxn*maxn],v[maxn*maxn];
vector<nod> edge[maxn];
void dfs(int now,int fa,int b,int f)
{
//	cerr<<now<<" "<<fa<<" "<<b<<" "<<f<<endl;
	vis[now]=tim; bel[fa][now][f]=b;
	int len=edge[now].size();
	for(int i=0;i<len;i++)
		if(vis[edge[now][i].nex]!=tim)
			dfs(edge[now][i].nex,fa,b,f);
}
void solve(int now)
{
	tim++; vis[now]=tim;
	int len=edge[now].size();
	for(int i=0;i<len;i++)
		if(vis[edge[now][i].nex]!=tim)
			dfs(edge[now][i].nex,now,edge[now][i].id,0);
 
	reverse(edge[now].begin(),edge[now].end());
 
	tim++; vis[now]=tim;
	for(int i=0;i<len;i++)
		if(vis[edge[now][i].nex]!=tim)
			dfs(edge[now][i].nex,now,edge[now][i].id,1);
}
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d",&u[i],&v[i]);
		edge[u[i]].push_back(nod(v[i],i));
	}
	for(int i=1;i<=n;i++)
		solve(i);
	for(int i=1;i<=m;i++)
	{
		if( (bel[v[i]][u[i]][0]!=0) ^ _ ^ (bel[u[i]][v[i]][0]!=i || bel[u[i]][v[i]][1]!=i) )
			printf("diff\n");
		else
			printf("same\n");
	}
}

         

Especially For U 

By ZRX


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值