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.csdn.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 }
View Code

思路:除了像普通的并查集定义一个 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 来源:CSDN 原文:https://blog.csdn.net/freezhanacmore/article/details/8774033 版权声明:本文为博主原创文章,转载请附上博文链接!

猜你喜欢

转载自www.cnblogs.com/codeoosacm/p/10078856.html
今日推荐