并查集专题2
hdu 1829 A Bug's Life
http://acm.hdu.edu.cn/showproblem.php?pid=1829
解题思路:这个题目跟其他的不太一样,给出的不是同类而是异类,其实是差不多的。
增加一个sex[i]表示i的异类,那么就是有两个集合,输入一组数据的时候判断一下两个是不是在同一个集合里,如果是那么输出Suspicious bugs found!
一直没有输出No suspicious bugs found!
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #define maxlen 2010 5 using namespace std; 6 int father[maxlen]; 7 int sex[maxlen]; 8 int n,m; 9 void init() 10 { 11 for(int i=0; i<=n; ++i) 12 { 13 father[i]=i; 14 sex[i]=0; 15 } 16 } 17 int Find(int x) 18 { 19 return father[x]==x?x:father[x]=Find(father[x]); 20 } 21 void Union(int x ,int y) 22 { 23 x=Find(x),y=Find(y); 24 if(x!=y) 25 father[x]=y; 26 } 27 int main () 28 { 29 int t; 30 int x,y; 31 scanf("%d",&t); 32 for(int Case=1; Case<=t; ++Case) 33 { 34 int flag=1; 35 scanf("%d%d",&n,&m); 36 init(); 37 for(int i=0; i<m; ++i) 38 { 39 scanf("%d%d",&x,&y); 40 if(Find(x)==Find(y)) 41 flag=0; 42 if(sex[x]==0) 43 sex[x]=y; 44 else 45 Union(sex[x],y); 46 if(sex[y]==0) 47 sex[y]=x; 48 else 49 Union(sex[y],x); 50 } 51 printf("Scenario #%d:\n",Case); 52 if(flag) 53 printf("No suspicious bugs found!\n\n"); 54 else 55 printf("Suspicious bugs found!\n\n"); 56 } 57 }
hdu 4496 D-City
http://acm.hdu.edu.cn/showproblem.php?pid=4496
解题思路:通化邀请赛的题目,并查集。
题目说的是N个点M条边,现在让你输出的是删除前k条边后,这个图的联通分支数是多少。
倒过来做,一开始的时候每个节点都是一个联通分支,所以分支数是N,然后每次加入一条边的时候,看一下这条边的两个端点是不是在同一个分支里,如果不是,合并并且分支数-1,将分支压入栈中。
最后输出就可以了。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <stack> 5 #define maxlen 10010 6 using namespace std; 7 int father[maxlen]; 8 int n,m; 9 char str[100010]; 10 struct node 11 { 12 int s,e; 13 }p[100010]; 14 void init() 15 { 16 for(int i=0;i<=n;++i) 17 father[i]=i; 18 } 19 int inline Find(int x) 20 { 21 return father[x]==x?x:father[x]=Find(father[x]); 22 } 23 void inline Union(int x,int y) 24 { 25 x=Find(x),y=Find(y); 26 if(x!=y) 27 father[x]=y; 28 } 29 int main () 30 { 31 while(scanf("%d%d",&n,&m)!=EOF) 32 { 33 for(int i=0;i<m;++i) 34 scanf("%d%d",&p[i].s,&p[i].e); 35 init(); 36 int cnt=n; 37 stack<int>s; 38 for(int i=m-1;i>=0;--i) 39 { 40 s.push(cnt); 41 if(Find(p[i].s)!=Find(p[i].e)) 42 { 43 Union(p[i].s,p[i].e); 44 cnt--; 45 } 46 } 47 while(!s.empty()) 48 { 49 printf("%d\n",s.top()); 50 s.pop(); 51 } 52 } 53 54 }
uva 11987 Almost Union-Find
题目大意:
给了三种操作:
1 p q 合并
2 p q 把p移动到q集合中
3 p 求p所在集合的元素个数以及元素之和
分析:
关键就是第二个操作,事实上移动操作就是删除+合并操作。
删除操作具体可以看我上一篇并查集专题最后一题
可以增加一个id[i]数组表示i这个数现在真实的对应的下标,在删除操作的时候,将id改为新的虚拟父亲,从新初始化既可以了。
其他两个值得维护也比较简单,就是在合并的是后把被合并的两个值加到合并的集合中去就可以了。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #define maxlen 200010 5 using namespace std; 6 int father[maxlen],id[maxlen]; 7 int num[maxlen]; 8 long long sum[maxlen]; 9 int n,m; 10 int cnt; 11 void init() 12 { 13 for(int i=1;i<=n;++i) 14 { 15 id[i]=father[i]=sum[i]=i; 16 num[i]=1; 17 } 18 cnt=n; 19 } 20 int Find(int x) 21 { 22 return father[x]==x?x:father[x]=Find(father[x]); 23 } 24 void Union(int x,int y) 25 { 26 x=Find(id[x]),y=Find(id[y]); 27 father[x]=y; 28 num[y]+=num[x]; 29 sum[y]+=sum[x]; 30 } 31 void Delete(int x) 32 { 33 int xx=Find(id[x]); 34 --num[xx]; 35 sum[xx]-=x; 36 id[x]=(++cnt);//从新初始化 37 father[id[x]]=id[x]; 38 num[id[x]]=1; 39 sum[id[x]]=x; 40 } 41 int main() 42 { 43 while(scanf("%d%d",&n,&m)!=EOF) 44 { 45 init(); 46 for(int i=0;i<m;++i) 47 { 48 int cmd,x,y; 49 scanf("%d",&cmd); 50 if(cmd==1) 51 { 52 scanf("%d%d",&x,&y); 53 if(Find(id[x])!=Find(id[y])) 54 Union(x,y); 55 } 56 else if(cmd==2) 57 { 58 scanf("%d%d",&x,&y); 59 if(Find(id[x])!=Find(id[y])) 60 { 61 Delete(x); 62 Union(x,y); 63 } 64 } 65 else 66 { 67 scanf("%d",&x); 68 x=Find(id[x]); 69 printf("%d %lld\n",num[x],sum[x]); 70 } 71 } 72 } 73 }