POI2008 BZOJ 1116 CLO -并查集-基环树

来源:互联网 发布:宽带网络测试仪 编辑:程序博客网 时间:2024/06/05 05:37

题目链接:右转进入题目

题目大意:给定一个无向图,选择若干条边,并变成有向的,使每个点的入度为1.

题解:因为最后是有向图,点的入度=1意味着图的入度为n意味着图的出度=n意味着图的度为2n意味着图中有n条边意味着这是个基环树森林。

显然对于每一个联通块,都检查是否能组成基环树即可。

显然如果组成基环树,那么一定有符合题目要求的做法。

则么检查呢?非常简单。先生成这个联通块的生成树(有块的点数-1条边的生成树),然后看看这个联通块是否还有其他没有加入的边,显然如果有就一定会变成基环树

检查是否还有其他的边其实没必要记录每个联通块的点数并和边数比较。直接再扫一遍所有没有加入的边(ui,vi),这次扫描一定有find_father(ui)==find_father(vi)这个性质。

然后每个联通块的生成树只加一次边就变成了基环树,就不能再加边了。

最后检查两次加边是否加了n条边即可。

代码:

//POI2008 BZOJ 1116#include<iostream>#include<algorithm>#include<cstdio>#include<cstring>#define MAXN 100010#define MAXM 1000010using namespace std;int fa[MAXN],u[MAXM],v[MAXM];bool canins[MAXN],choose[MAXM];inline int findf(int x){int fx=x,y;while(fx!=fa[fx]) fx=fa[fx];while(x!=fx) y=fa[x],fa[x]=fx,x=y;return fx;}inline void merge_union(int x,int y){fa[x]=y;return;}int main(){int n,m;scanf("%d%d",&n,&m);for(int i=1;i<=m;i++)scanf("%d%d",&u[i],&v[i]);for(int i=1;i<=n;i++)canins[i]=true,fa[i]=i;memset(choose,false,sizeof(choose));int ingraph=0,cur=1;while(cur<=m){int fu=findf(u[cur]),fv=findf(v[cur]);if(fu!=fv){merge_union(fu,fv);choose[cur]=true;ingraph++;}cur++;}cur=1;while(cur<=m){if(choose[cur]){cur++;continue;}int fu=findf(u[cur]),fv=findf(v[cur]);if(fu==fv&&canins[fu]){canins[fv]=false;ingraph++;}cur++;}if(ingraph==n) printf("TAK\n");else printf("NIE\n");return 0;}


0 0