【ACM回顾】并查集

以前学的不带权并查集相当简单,这次回顾主要学习一下 带权并查集

这里就要注意到并查集的本质——树,这是贯穿始终的思想

今天主要讲讲并查集的合并和路径压缩,空谈无益直接上题

A-Wireless Network (poj2236)

B-The Suspect (poj1611)

C-How Many Tables (hdu1213)

  以上都是毫无营养的 不带权并查集

D-How Many Answers Are Wrong (HDU 3038)

  两个人在玩一个很无聊的游戏,一个人选出数列中的一段求和并告诉对方,另一个人判断与之前给出的信息是否有矛盾

  实际上sum(l,r) = presum(r) - presum(l-1),实际上表示的是第r个位置和第l-1个位置前缀和的关系

  而这种关系,可以通过边权表示出来,只需要设定含义即可,此处设定为 子节点 减 父节点 之差

  检验的时候,如果r和l-1属于同一个并查集,那么说明它们肯定存在某种关系(由题意这种关系已确定)

  路径压缩时,只要边权能够做到等效即可,比如A连B边权为u,B连C边权为v,那么本题中含义就是B与A之差为u,C与B之差为v,那么直接把C连到A上取边权为u+v即可

  getf操作在此处的作用不仅是取root,还顺便对root到当前点之间的边权进行求和,效率起见,直接把路径上的点通过递归全接到root上,只需要保证等效的边权正确即可

  一点要注意,当我们合并x,y所在的两个并查集时,我们原本希望是连一条节点(l-1)到节点r的边权为sum的边

  但是合并这两棵树时我们只能对根节点进行操作,f[y.root] = x.root,此处一定要区别x,y和x.root,y.root之间的区别,边权要经过换算

  此处的等效边权为sum - y.edge + x.edge(类比向量的计算)

 1 #include<cstdio>
 2 #include<utility>
 3 using namespace std;
 4 
 5 const int N = 200010;
 6 
 7 typedef pair<int, int> pii;
 8 int n,m;
 9 pii f[N];
10 
11 pii getf(int p)
12 {
13     if (f[p].first == p)
14     {
15         return pii(p,0);
16     }
17     else
18     {
19         pii retf = getf(f[p].first);
20         f[p].first = retf.first;
21         f[p].second = retf.second + f[p].second;
22         return f[p];
23     }
24 }
25 
26 int main()
27 {
28     #ifndef ONLINE_JUDGE
29         freopen("hdu3038.in","r",stdin);
30     #endif
31     while(scanf("%d %d",&n,&m) != EOF)
32     {
33         int ans = 0;
34         for(int i=0;i<=n;i++) f[i].first = i, f[i].second=0;
35         for(int i=1;i<=m;i++)
36         {
37             int l,r,sum;
38             scanf("%d %d %d",&l,&r,&sum);
39             l--;
40             pii lf = getf(l), rf = getf(r);
41             if(lf.first == rf.first)
42             {
43                 if(rf.second - lf.second != sum) ans++;
44                 continue;
45             }
46             f[rf.first].first = lf.first;
47             f[rf.first].second = sum - rf.second + lf.second;
48         }
49         printf("%d\n",ans);
50     }
51     return 0;
52 }
View Code

 

E-食物链 (poj1182)

  你知道这题和上题相比最特殊的是什么吗。

  它是中文的。

  开玩笑的...其实一个道理。注意边权的等效,区分x和x.root的边权的意义即可

 1 #include<cstdio>
 2 #include<utility>
 3 using namespace std;
 4 
 5 const int N = 50000 + 10; 
 6 
 7 int n,k;
 8 typedef pair<int, int> pii;
 9 pii D[N];
10 
11 pii getf(int p)
12 {
13     if (D[p].first == p)
14     {
15         return pii(p, 0);
16     }
17     else
18     {
19         pii ret = getf(D[p].first);
20         D[p].first = ret.first;
21         D[p].second = (D[p].second + ret.second) % 3;
22         return D[p];
23     }
24 }
25 
26 int main()
27 {
28     #ifndef ONLINE_JUDGE
29         freopen("poj1182.in","r",stdin);
30     #endif
31     int ans = 0;
32     scanf("%d %d",&n,&k);
33     for(int i=1;i<=n;i++) D[i].first=i, D[i].second=0;
34     while(k--)
35     {
36         int opt,x,y;
37         scanf("%d %d %d",&opt,&x,&y);
38         if (x>n || y>n)
39         {
40             ans++;
41             continue;
42         }
43         pii fx = getf(x), fy = getf(y);
44         if (opt == 1)
45         {
46             if(fx.first == fy.first)
47             {
48                 if(fx.second != fy.second) ans++;
49                 continue;
50             }
51             D[fy.first].first = fx.first;
52             D[fy.first].second = (fx.second + 3 - fy.second) % 3;
53         }
54         if (opt == 2)
55         {
56             if(x==y) {ans++; continue;}
57             if(fx.first == fy.first)
58             {
59                 if((3 + fy.second - fx.second) % 3 != 1) ans++;
60                 continue;
61             }
62             D[fy.first].first = fx.first;
63             D[fy.first].second = (fx.second + 4 - fy.second) % 3;
64         }
65     }
66     printf("%d\n",ans);
67     
68     return 0;
69 } 
View Code

 

posted @ 2017-07-10 16:24  K.Nick  阅读(253)  评论(0编辑  收藏  举报
$a = (1-\sin(\theta))$