poj1703 并查集,维护不同集合
题目大意:
警方决定捣毁两大犯罪团伙:龙帮和蛇帮,显然一个帮派至少有一人。该城有N个罪犯,编号从1至N(N<=100000。将有M(M<=100000)次操作。
D a b 表示a、b是不同帮派
A a b 询问a、b关系
对于每一个A操作,回答"In the same gang."或"In different gangs." 或"Not sure yet."
(大意摘自vjudge)
题解:维护不同集合关系的经典题目,和poj食物链那道题一样
可参考https://blog.****.net/freezhanacmore/article/details/8774033
直接上代码
1 #include<cstdio> 2 #include<cstring> 3 #include<iostream> 4 using namespace std; 5 const int maxn=1e5+100; 6 int head=0,tail=10002; 7 int n,m; 8 int father[maxn*2],rank[maxn*2],vis[maxn*2]; 9 int findf(int x) 10 { 11 int k, j, r; 12 r = x; 13 while(r != father[r]) 14 r = father[r]; 15 k = x; 16 while(k != r) 17 { 18 j = father[k]; 19 father[k] = r; 20 k = j; 21 } 22 return r; 23 } 24 25 void merg(int x,int y) 26 { 27 int fx=findf(x); 28 int fy=findf(y); 29 if(rank[fx]<=rank[fy]) 30 { 31 father[fy]=fx; 32 rank[fx]++; 33 } 34 else 35 { 36 father[fx]=fy; 37 rank[fy]++; 38 } 39 } 40 void ini() 41 { 42 for(int i=0;i<=2*maxn;i++) father[i]=i; 43 father[tail]=tail; 44 memset(rank,0,sizeof(rank)); 45 } 46 void solve() 47 { 48 scanf("%d%d",&n,&m); 49 ini(); 50 for(int i=1;i<=m;i++) 51 { 52 char a;int b,c; 53 cin>>a;scanf("%d%d",&b,&c); 54 if(a=='A') 55 { 56 //for(int i=1;i<=n;i++) printf("%d ",father[i]);puts(""); 57 if(findf(b)==findf(c)||findf(b+n)==findf(c+n)) 58 puts("In the same gang."); 59 else if(findf(b)==findf(c+n)||findf(c)==findf(b+n)) 60 puts("In different gangs."); 61 else puts("Not sure yet."); 62 } 63 else 64 { 65 merg(b,c+n); 66 merg(c,b+n); 67 } 68 } 69 } 70 71 int main() 72 { 73 //freopen("in.txt","r",stdin); 74 int T; 75 cin>>T; 76 for(int i=1;i<=T;i++) solve(); 77 }
思路:除了像普通的并查集定义一个 p[] 记录父亲节点外,还定义一个 r[] 记录当前点与其所属的连通分量的根节点的关系。 r[] = 0 表示属于同一个帮派; r[] = 1表示与其根节点属于不同的帮派。 开始时初始化自己是自己的父亲 p[x] = x,自己与自己属于同一类 r[x] = 0. 一旦输入 D 断定 x 和 y 属于不同集合后,就连接 x 和 y 所在的树,同时更新 r[] 一旦输入 A 如果 find(x) 不等于 find(y) 说明还没有判断过 x 与 y 直接输出关系不确定即可 Not sure yet. 如果find(x)等于 find(y) ,但是他们的r不等,说明属于不同帮派,输出In different gangs. 如果他们的r相等,说明属于同一个帮派,则输出In the same gang注意:1.find()函数寻找根节点的时候要不断的更新 r 根据子节点与父亲节点的关系和父节点与爷爷节点的关系,推导子节点与爷爷节点的关系 如果 a 和 b 的关系是 r1, b 和 c 的关系是 r2, 那么 a 和 c 的关系就是 (r1+r2)%2 . PS:因为只用两种情况所以对 2 取模。 如果实在不好理解,那么我们就枚举推理一下,共有 2*2 = 4种情况: (a, b) (b, c) (a, c) (r1+r2)%2 0 0 0 0 a 和 b是同类 , b 和 c 是同类, 所以 a 和 c 也是同类 0 1 1 1 a 和 b是同类 , b 和 c 是异类, 所以 a 和 c 也是异类 1 0 1 1 a 和 b是异类 , b 和 c 是同类, 所以 a 和 c 是异类 1 1 0 0 a 和 b是异类 , b 和 c 是异类, 所以 a 和 c 是同类 2.Union()联合两棵树的时候也要更新两棵树的根的关系 定义:fx 为 x的根节点, fy 为 y 的根节点 联合时,使得 p[fx] = fy; 同时也要寻找 fx 与 fy 的关系。关系为:(r[x]+r[y]+1)%2 如何证明? fx 与 x 的关系是 r[x], x 与 y 的关系是 1 (因为确定是不同类,才联合的), y 与 fy 关系是 r[y],模 2 是因为只有两种关系 所以又上面的一点所推出的定理可以证明 fx 与 fy 的关系是: (r[x]+r[y]+1)%2--------------------- 作者:cfreezhan 来源:**** 原文:https://blog.****.net/freezhanacmore/article/details/8774033 版权声明:本文为博主原创文章,转载请附上博文链接!