续并查集学习笔记——Gang团伙题解
一言不合先贴题目
Description
在某城市里住着n个人,任何两个认识的人不是朋友就是敌人,而且满足: 1、 我朋友的朋友是我的朋友; 2、 我敌人的敌人是我的朋友; 所有是朋友的人组成一个团伙。告诉你关于这n个人的m条信息,即某两个人是朋友,或者某两个人是敌人,请你编写一个程序,计算出这个城市最多可能有多少个团伙?
Input
第1行为n和m,N小于1000,M小于5000; 以下m行,每行为p x y,p的值为0或1,p为0时,表示x和y是朋友,p为1时,表示x和y是敌人。
Output
一个整数,表示这n个人最多可能有几个团伙。
Sample Input
6
4
E 1 4
F 3 5
F 4 6
E 1 2
4
E 1 4
F 3 5
F 4 6
E 1 2
Sample Output
3
HINT
{1},{2,4,6},{3,5}
一开始的想法和ABC野兽那道题一样,可以维护每个点和祖先的关系。而且似乎更简单。于是有了下面的程序
#include<iostream> #include<cstdio> using namespace std; int fa[1010],deep[1010],ans[1010][2],n,m; int getf(int k){ if(fa[k]!=k){ int t=fa[k]; fa[k]=getf(fa[k]); deep[k]=deep[k]+deep[t]; deep[k]=deep[k]%2; } return fa[k]; } int main(){ cin>>n>>m; for(int i=1;i<=n;++i){ fa[i]=i; deep[i]=0; } for(int i=1;i<=m;++i){ char c[2]; int x,y; scanf("%s%d%d",&c,&x,&y); if((c[0]=='1')&&(getf(x)!=getf(y))){ int fx=getf(x); int fy=getf(y); deep[fx]=(deep[y]-deep[x]+3)%2; fa[fx]=fy; } if((c[0]=='0')&&(getf(x)!=getf(y))){ int fx=getf(x); int fy=getf(y); deep[fx]=(deep[y]-deep[x]+2)%2; fa[fx]=fy; } } for(int i=1;i<=n;++i){ fa[i]=getf(i); //cout<<fa[i]<<" "<<deep[i]<<endl; ans[fa[i]][deep[i]]++; } int add=0; for(int i=1;i<=n;++i) for(int j=0;j<=1;++j)if(ans[i][j]>0){ add++; } cout<<add; return 0; }
然后、、、、、、就爆0了。
哪里出了问题?忽然发现题设中并没有:我的敌人的朋友是我的敌人。MDZZ!这不符合逻辑啊,虽然NOI从来不讲逻辑。
重新出发,想到分点的方法。通过朋友相关联的,fa[x]=y;通过敌人相关联的,fa[x]=y+n,fa[x+n]=y。该题完美解决。楼下程序:
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; int n,m,ans,a[1010]; int fa[2020]; int Find(int x) { if(!fa[x]||fa[x]==x) return fa[x]=x; return fa[x]=Find(fa[x]); } void Union(int x,int y) { x=Find(x);y=Find(y); if(x==y) return ; fa[x]=y; } int main(){ int i,x,y; char p[10]; cin>>n>>m; for(i=1;i<=m;i++) { scanf("%s%d%d",p,&x,&y); if(p[0]=='F') Union(x,y); else Union(x,y+n),Union(x+n,y); } for(i=1;i<=n;i++) a[i]=Find(i); sort(a+1,a+n+1); for(i=1;i<=n;i++) if(i==1||a[i]!=a[i-1]) ++ans; cout<<ans<<endl; return 0; }
To be continue......