hdu 2473 Junk-Mail Filter

https://vjudge.net/problem/HDU-2473

题意:

有一堆垃圾邮件需要识别。一开始每封邮件是互相不关联的。给出两种操作,第一种是指出两封邮件具有相同的特征,即两封邮件关联,且这种关系是传递的。第二种是指出某封邮件被误判,要求它断绝与其他所有邮件的关系,最后问一共有多少种互相不关联的邮件。

思路:

这种题一开始就看出来要用并查集,但是涉及到了删除操作,就比较困难,解决并查集单个节点的操作,有一种方法,叫做设立虚父马甲,这是从这题学到的。

首先,看第一种方法,直接把孤立的节点设为自己。例如,有5封邮件,2,3,4,5的根均为1,当1被孤立的时候,par[1] = 1。2,3,4,5向上查询,查到的根还是为1,这种方法不可行。

再来看第二种方法,把孤立的节点的父亲随便设置为一个不存在的值。例如1,2,3,4,5的根均为4,那么当4被孤立的时候,设置par[4] = 7,然后其他点向上查询,则都是查到的7,还是没有达到孤立的效果,这个方法也是不可行的。

接下来看看设置虚父马甲的方法。这个方法是在初始化的时候把每一个节点i的父亲par[i]  设置为 i+n,然后把2 * n 到 2 * n + m的父亲设置为自己,为什么是2 * n + m呢?因为一共有m种操作,如果所有的操作都是孤立,那么就可能用到m个额外的虚父节点。举一个例子,有0,1,2,3,4,5一共6封信,每封信的初始化后父亲分别是6,7,8,9,10,11,现在假设是以2为根,那么实际情况就是0,1,2,3,4,5的父亲节点都是8,现在,将2孤立,现在让par[2] = id++,(id是从2*n开始的,他代表额外的新的未使用过的父亲节点),par[2] = 12,但是其他的点的父亲还是8,所以就达到了可以孤立点的目的,这是一种极为巧妙的方法。具体还是详见代码

代码:

 1 #include <stdio.h>
 2 #include <string.h>
 3 
 4 const int N = 100005;
 5 const int M = 1000005;
 6 
 7 int par[N+N+M];
 8 bool vis[N+N+M];
 9 
10 int fin(int x)
11 {
12     if (x == par[x]) return x;
13     else return par[x] = fin(par[x]);
14 }
15 
16 void unit(int x,int y)
17 {
18     x = fin(x);
19     y = fin(y);
20 
21     if (x != y) par[x] = y;
22 }
23 
24 int main()
25 {
26     int n,m;
27 
28     int cas = 0;
29 
30     while (scanf("%d%d",&n,&m) != EOF)
31     {
32         if (m == 0 && n == 0) break;
33 
34         printf("Case #%d: ",++cas);
35 
36         memset(vis,0,sizeof(vis));
37 
38         for (int i = 0;i < n;i++)
39             par[i] = i + n;
40 
41         for (int i = n;i < n + n + m;i++)
42             par[i] = i;
43 
44         int id = n + n;
45 
46         for (int i = 0;i < m;i++)
47         {
48             char op;
49             int x,y;
50 
51             scanf(" %c%d",&op,&x);
52 
53             if (op == 'M')
54             {
55                 scanf("%d",&y);
56 
57                 unit(x,y);
58             }
59             else
60             {
61                 par[x] = id++;
62             }
63         }
64 
65         int ans = 0;
66 
67         for (int i = 0;i < n;i++)
68         {
69             if (!vis[fin(i)])
70             {
71                 ans++;
72                 vis[fin(i)] = 1;
73             }
74         }
75 
76         printf("%d\n",ans);
77     }
78 
79     return 0;
80 }

 

posted @ 2017-07-20 15:10  qrfkickit  阅读(167)  评论(0编辑  收藏  举报