给定一个 个点 条边的图 ,有一些边有向,剩下的边无向。你要给每条无向边定向使得定向后的有向图 强连通。输出方案。
。
Jerry Wen 定理,考虑找有解的若干必要/充分条件,再证明这些条件的并是充要的:
- 若把所有有向边看作无向边,则 一定是一个点双;
- 若把所有无向边看作双向边(两条有向边: 和 ),则 一定强连通;
这些条件的并显然是必要的,考虑证明它们的并是充要的:
- 
必要性显然; 
- 
充分性: 考虑反证。若某个时刻满足这两个条件,且对于某条无向边 ,无论如何定向都不能同时满足这两个条件,则: - 
由于第一个条件定向后一定仍满足,所以一定是不满足第二个条件; 
- 
由于定向前满足第二个条件,定向后不满足,所以 与 一定都要被经过,那么图一定是长这样的:  但是这样显然可以把这两个环合并得到一个大环,不需要走 这条边。 所以不存在这种情况。 Q.E.D. 
 
- 
由于题目保证了一定有解,所以第一个条件一定满足,只用判断第二个条件是否满足。
那么直接枚举每条边, 判断哪种定向合法即可,时间复杂度 。注意判合法只要判断定向后 和 能否互相到达。
代码如下:
#include <iostream>
#include <cstdio>
#include <vector>
using namespace std;
const int S=5005;
struct node
{
	int x,y,t;
}ed[S];
int n,m;
vector<int> g[S];
bool vis[S];
int ans[S];
bool chk(int u,int v,int ban)
{
	vis[u]=true;
	if(u==v) return true;
	for(int i:g[u])
	{
		if(i==ban) continue;
		int too=-1;
		if(ed[i].x==u)
		{
			if(ed[i].t==0||ed[i].t==1) too=ed[i].y;
		}
		else
		{
			if(ed[i].t==0||ed[i].t==2) too=ed[i].x;
		}
		if(v!=-1&&!vis[too]&&chk(too,v,ban)) return true;
	}
	return false;
}
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++) scanf("%d%d%d",&ed[i].x,&ed[i].y,&ed[i].t);
	for(int i=1;i<=m;i++) g[ed[i].x].push_back(i),g[ed[i].y].push_back(i);
	for(int i=1;i<=m;i++)
	{
		if(ed[i].t==0)
		{
			for(int j=1;j<=n;j++) vis[j]=false;
			if(chk(ed[i].y,ed[i].x,i)) ed[i].t=1,ans[i]=0;
			else ed[i].t=2,ans[i]=1;
		}
		else ans[i]=0;
	}
	for(int i=1;i<=m;i++) printf("%d\n",ans[i]);
	return 0;
}
