POI 2008 CLO-Toll

POI 2008 CLO-Toll

洛谷传送门

题意翻译

题目描述:

给你 nn 个点和 mm 条双向边,问能否将其中的一些边改成有向边,使得只考虑有向边的情况下每个点的入度都为 11 。

输入格式:

第一行输入 n,m(1≤n≤100000,1≤m≤200000)n,m(1≤n≤100000,1≤m≤200000) ,接下来 mm 行每行两个数 a,ba,b 表示点 aa 和点 bb 之间有一条双向边。输入保证没有重边与自环。

输出格式:

若没有合法方案,输出 NIENIE ,否则先在第一行输出 TAKTAK ,然后在第 i+1i+1 行输出点 ii 的入度是由哪个点出发的边所得到的。

感谢@hdxrie 提供的翻译


题解:

其实很简单的。就是在每个连通块里拎出来基环树。

如果拎不出连通块,那就是NIE。

然后考虑怎么拎基环树。

基环树是啥啊,就是一棵树加上任意一条边呗。这条边就是“返祖边”啊。所以我们只需要对每个连通块DFS一遍,只拎一条返祖边(这个用flag维护),碰到返祖边,就说明可以和一些节点构成一个环,这样的话就回溯,把所有沿途节点全部反向即可。

代码:

#include<cstdio>
using namespace std;
const int maxn=1e5+5;
const int maxm=2e5+5;
int n,m;
int tot,head[maxn],nxt[maxm<<1],to[maxm<<1];
int fa[maxn],ans[maxn];
bool v[maxn];
bool flag;
void add(int x,int y)
{
	to[++tot]=y;
	nxt[tot]=head[x];
	head[x]=tot;
}
void dfsback(int x,int s)
{
	if(ans[x])
		dfsback(ans[x],x);
	ans[x]=s;
}
void dfs(int x)
{
	v[x]=1;
	for(int i=head[x];i;i=nxt[i])
	{
		int y=to[i];
		if(y==fa[x])
			continue;
		if(v[y])
		{
			if(!flag)
			{
				flag=1;
				dfsback(y,x);
			}
		}
		else
		{
			ans[y]=fa[y]=x;
			dfs(y);
		}
	}
}
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++)
	{
		int x,y;
		scanf("%d%d",&x,&y);
		add(x,y);
		add(y,x);
	}
	for(int i=1;i<=n;i++)
		if(!v[i])
		{
			flag=0;
			dfs(i);
			if(!flag)
			{
				puts("NIE");
				return 0;
			}
		}
	puts("TAK");
	for(int i=1;i<=n;i++)
		printf("%d\n",ans[i]);
	return 0;
}
posted @ 2020-12-04 09:52  Seaway-Fu  阅读(96)  评论(0编辑  收藏  举报