并查集专题

1、poj 2236 Wireless Network

  题意:一张图上分布着n台坏了的电脑,并知道它们的坐标。两台修好的电脑如果距离<=d就可以联网,也可以通过其他修好的电脑间接相连。给出操作“O x”表示修好x,给出操作“S x y”,请你判断x和y在此时有没有连接上。

  思路:简单并查集。

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<memory.h>
 4 #include<cmath>
 5 using namespace std;
 6 int n, d;
 7 const int maxn = 1200;
 8 int pre[maxn];
 9 struct node
10 {
11     int x;
12     int y;
13 }cpt[maxn];
14 int Find(int x)
15 {
16     int r = x;
17     while (r != pre[r])
18     {
19         r = pre[r];
20     }
21     int i = x, j;
22     while (pre[i] != r)
23     {
24         j = pre[i];
25         pre[i] = r;
26         i = j;
27     }
28     return r;
29 }
30 void Join(int x, int y)
31 {
32     int px = Find(x), py = Find(y);
33     if (px != py) pre[px] = py;
34 }
35 int main()
36 {
37     while (~scanf("%d%d", &n, &d))
38     {
39         for (int i = 1; i <= n; i++) scanf("%d%d", &cpt[i].x, &cpt[i].y);
40         memset(pre, 0, sizeof(pre));
41         char op[3];
42         while (~scanf("%s", op))
43         {
44             if (op[0] == 'O')
45             {
46                 int p;
47                 scanf("%d", &p);
48                 pre[p] = p;
49                 for (int i = 1; i <= n; i++)
50                 {
51                     if (i == p)continue;
52                     double dis = sqrt(1.0*(cpt[i].x - cpt[p].x)*(cpt[i].x - cpt[p].x) + (cpt[i].y - cpt[p].y)*(cpt[i].y - cpt[p].y));
53                     if (dis <= d&&pre[i]!=0)
54                     {
55                         Join(i, p);
56                     }
57                 }
58             }
59             else
60             {
61                 int p, q;
62                 scanf("%d%d", &p, &q);
63                 if (Find(p) != Find(q))printf("FAIL\n");
64                 else printf("SUCCESS\n");
65             }
66         }
67     }
68     return 0;
69 }
View Code

2、poj 1611 The Suspects

  题意:有一个学校,有N个学生,编号为0-N-1,现在0号学生感染了非典,凡是和0在一个社团的人就会感染,并且这些人如果还参加了别的社团,他所在的社团照样全部感染,求感染的人数。

  思路:并查集,合并的时候顺便统计人数。

 1 #include<iostream>
 2 using namespace std;
 3 const int maxn = 30010;
 4 int pre[maxn];
 5 int Cnt[maxn];
 6 int Find(int x)
 7 {
 8     int r = x;
 9     while (r != pre[r])
10     {
11         r = pre[r];
12     }
13     int i = x, j;
14     while (pre[i] != r)
15     {
16         j = pre[i];
17         pre[i] = r;
18         i = j;
19     }
20     return r;
21 }
22 void Join(int x, int y)
23 {
24     int fx = Find(x), fy = Find(y);
25     if (fx != fy) pre[fx] = fy,Cnt[fy]+=Cnt[fx];
26 }
27 int main()
28 {
29     int n, m;
30     while (~scanf("%d%d", &n, &m))
31     {
32         for (int i = 0; i <= n; i++) pre[i] = i,Cnt[i]=1;
33         if (n == 0 && m == 0)break;
34         for (int i = 0; i < m; i++)
35         {
36             int k;
37             scanf("%d", &k);
38             int pre, now;
39             for (int i = 0; i < k; i++)
40             {
41                 int x;
42                 scanf("%d", &x);
43                 if (i == 0) pre = now = x;
44                 else
45                 {
46                     now = x;
47                     Join(now, pre);
48                     pre = now;
49                 }
50             }
51         }
52         //int cnt = 0;
53         //for (int i = 0; i < n; i++)
54         //{
55         //    if (Find(i) == Find(0)) cnt++;
56         //}
57         printf("%d\n", Cnt[Find(0)]);
58     }
59     return 0;
60 }
View Code

3、hdu 1213 How Many Tables

  题意:两个人若互相认识,则坐在同一张桌子上。问需要多少张桌子。

  思路:并查集,合并的时候更新剩余连通块。

 1 #include<iostream>
 2 using namespace std;
 3 const int maxn = 1010;
 4 int pre[maxn];
 5 int cnt;
 6 int Find(int x)
 7 {
 8     int r = x;
 9     while (r != pre[r])
10     {
11         r = pre[r];
12     }
13     int i = x, j;
14     while (pre[i] != r)
15     {
16         j = pre[i];
17         pre[i] = r;
18         i = j;
19     }
20     return r;
21 }
22 void Join(int x, int y)
23 {
24     int fx = Find(x), fy = Find(y);
25     if (fx != fy) pre[fx] = fy, cnt--;
26 }
27 int main()
28 {
29     int t;
30     scanf("%d", &t);
31     while (t--)
32     {
33         int n, m;
34         scanf("%d%d", &n, &m);
35         cnt = n;
36         for (int i = 1; i <= n; i++) pre[i] = i;
37         for (int i = 0; i < m; i++)
38         {
39             int x, y;
40             scanf("%d%d", &x, &y);
41             Join(x, y);
42         }
43         printf("%d\n", cnt);
44     }
45     return 0;
46 }
View Code

4、hdu 3038 How Many Answers Are Wrong

  题意:给你M个区间和,问你有几个是与前面矛盾的。

  思路:带权并查集,利用并查集形成集合树,利用sum数组记录集合树中当前节点i到根之间的边的权值和,如果当前两个节点不在同一棵集合树中,那么通过两个集合树合并,调整权值,使能够保证合法的,所以只需要合并,将一棵树的根变为另一棵树的根的子节点,然后建边,因为f[i]<i,i<j,f[j]<j,所以s=sum[j]+sum[f[j]]-sum[i]因为当前j的根是f[j],所以要加上新加的边权,那么新的边权既可以通过这个方程求取即可。

 1 //利用并查集形成集合树,利用sum数组记录集合树中当前节点i到根之间的边的权值和,如果当前两个节点不在同一棵集合树中,那么通过两个集合树合并,调整权值,使能够保证合法的,所以只需要合并,将一棵树的根变为另一棵树的根的子节点,然后建边,因为f[i]<i,i<j,f[j]<j,所以s=sum[j]+sum[f[j]]-sum[i]因为当前j的根是f[j],所以要加上新加的边权,那么新的边权既可以通过这个方程求取即可
 2 #include<stdio.h>
 3 #define MAXN 200010
 4 int f[MAXN], r[MAXN];
 5 int find(int x)
 6 {
 7     if (x == f[x])
 8         return f[x];
 9     int t = find(f[x]);
10     r[x] = r[x] + r[f[x]];//这里首先要对这个递归的过程很了解,当递归到某一层时,x还未接到根节点上,所以r[x]表示的是x到f[x]的距离,经上一步操作,f[x]已经接到根节点上了,所以r[f[x]]表示的是父节点到根节点的距离所以x到根节点的距离就直接等于r[x]+r[f[x]]
11     f[x] = t;
12     return f[x];
13 }
14 int fun(int x, int y)
15 {
16     if (x>y)
17         return x - y;
18     else y - x;
19 }
20 int Union(int x, int y, int sum)
21 {//传进来时x<y
22     int a = find(x);
23     int b = find(y);
24     if (a == b)
25     {
26         if (fun(r[x], r[y]) == sum)
27             return 0;
28         else return 1;
29     }
30     else
31     {
32         f[a] = b;
33         r[a] = r[y] + sum - r[x];//r[y]表示y到b的距离,r[x]表示x到a的距离,sum表示x到y的距离,现在要将a接到b后面,那么r[a]很表示a到b的距离,很明显就是这个式子了   
34         return 0;
35     }
36 }
37 int main()
38 {
39     int n, m, i, ans, a, b, s;
40     while (scanf("%d %d", &n, &m) != EOF)
41     {
42         ans = 0;
43         for (i = 0; i <= n; i++)
44         {
45             f[i] = i;
46             r[i] = 0;
47         }
48         for (i = 1; i <= m; i++)
49         {
50             scanf("%d %d %d", &a, &b, &s);
51             a--;//左区间减一,方便处理
52             if (Union(a, b, s))
53                 ans++;
54         }
55         printf("%d\n", ans);
56     }
57     return 0;
58 }
View Code

5、poj 1182 食物链

  题意:有  3  种动物,互相吃与被吃,现在告诉你  m  句话,其中有真有假,叫你判断假的个数  (  如果前面没有与当前话冲突的,即认为其为真话  )。每句话开始都有三个数 D A B,当D = 1时,表示A 和B是同类,当D = 2时表示A 吃 B。

  思路:带权并查集。

 1 #include<iostream>
 2 using namespace std;
 3 const int maxn = 50010;
 4 int n,k;
 5 int pre[maxn], r[maxn];
 6 //根据“x与p[x]能否确定两者之间的关系”来划分,若能确定x与p[x]的关系,则它们同属一个集合。
 7 //p[x]表示x的根结点。r[x]表示p[x]与x的关系。r[x] == 0 表示p[x]与x同类;1表示p[x]吃x;2表示x吃p[x]。
 8 void Init()
 9 {
10     for (int i = 0; i <= n; i++)
11     {
12         pre[i] = i;
13         r[i] = 0;
14     }
15 }
16 
17 //每次通过儿子对父亲的关系,父亲对爷爷的关系,得到儿子对爷爷的关系
18 int Find(int x)
19 {
20     if (pre[x] == x) return x;
21     else
22     {
23         int fa = pre[x];
24         pre[x] = Find(fa);
25         r[x] = (r[x] + r[fa]) % 3;
26         return pre[x];
27     }
28 }
29 
30 bool Union(int x, int y, int d)
31 {//d=1:x和y是同类;d=2,x吃y
32     int rx = Find(x), ry = Find(y);
33     if (rx == ry)return true;
34     else
35     {
36         pre[ry] = rx;
37         r[ry] = (r[x] - r[y] + 2 + d) % 3;
38         //r[ry]=(3-r[y]+(d-1)+r[x])%3(可推)
39         return false;
40     }
41 }
42 
43 int main()
44 {
45     scanf("%d%d", &n, &k);
46     Init();
47     int cnt = 0;
48     while (k--)
49     {
50         int d, x, y;
51         scanf("%d%d%d", &d, &x, &y);
52         if (x > n || y > n) cnt++;
53         else
54         {
55             bool same = Union(x, y, d);
56             if (same)
57             {
58                 if (d == 1)
59                 {
60                     //D == 1 表示X与Y为同类,而从r[X] != r[Y]可以推出 X 与 Y 不同类。矛盾。
61                     if (r[x] != r[y])
62                     {
63                         cnt++;
64                     }
65                 }
66                 else
67                 {
68                     if ((r[y] + 3 - r[x]) % 3 != 1)
69                     {//(X 与Y为同类)或者 r[X] == ( r[Y] + 1 ) % 3 (Y吃X )则此话为假。
70                         cnt++;
71                     }
72                 }
73             }
74         }
75     }
76     printf("%d\n", cnt);
77     return 0;
78 }
View Code

6、poj 1417 True Liars(并查集+DP)

  题意:给出p1+p2个人,其中p1个是好人,p2个是坏人。然后有一些关系 ,a说b是好人(坏人).其中没有矛盾的,判断是否有唯一解判断哪些人是好人,哪些人是坏人。其中比较重要的是,好人总说真话,坏人总说假话。

  思路:

  ①一个人说另一个人是好人,那么如果这个人是好人,说明 对方确实是好人,如果这个是坏人,说明这句话是假的,对方也是坏人。

  ②如果一个人说另一个人是坏人,那么如果这个人是好人,说明对方是坏人,如果这个是坏人,说明 对方是好人。

也就是如果条件是yes说明这两个是相同集合的,否则是两个不同的集合。用r[i]表示i结点与根结点的关系,0为相同集合,1为不同集合。

通过并查集,可以将所有人分为若干个集合,其中对于每一个集合,又分为两个集合(好人和坏人,但是不知道哪些是好人,哪些是坏人,我们只有相对关系)

用dp[i][j]表示前i个集合中好人为j的种数,如果dp[cnt][p1]!=1说明方案不唯一,或者无解。belong[i][0]表示i属于哪个大类,belong[i][1]表示i属于该大类的哪一个小类,choose[i]倒推找出选择的大集合i的两类中是哪一类,然后升序输出好人的编号。

  1 #include<iostream>
  2 using namespace std;
  3 int n, p1, p2;
  4 const int maxn = 1100;
  5 int pre[maxn];//i的父亲
  6 int r[maxn];//i和pre[i]的关系
  7 //0:同类;1:不同类
  8 
  9 bool vis[maxn];
 10 
 11 int cntnum[maxn][2];//cntnum[i][j]表示把第i个集合分成两个部分的人数
 12 
 13 int dp[maxn][310];//dp[i][j]表示前i个集合中好人为j的种数
 14 
 15 int belong[maxn][2];//belong[i][0]表示i属于哪个大类,belong[i][1]表示i属于该大类的哪一个小类(对应cntnum)
 16 int id[maxn];
 17 //根据孩子与父亲,父亲与祖父之间的关系可以推出孩子与祖父的关系
 18 int Find(int x)
 19 {
 20     if (pre[x] == x) return x;
 21     else
 22     {
 23         int fa = pre[x];
 24         pre[x] = Find(fa);
 25         r[x] = r[x] ^ r[fa];
 26         return pre[x];
 27     }
 28 }
 29 
 30 void Union(int x, int y, int k)
 31 {
 32     int rx = Find(x), ry = Find(y);
 33     if (rx != ry)
 34     {
 35         pre[rx] = ry;
 36         r[rx] = (r[x] - r[y] + 2 + k) % 2;
 37     }
 38 }
 39 
 40 void Init()
 41 {
 42     for (int i = 0; i <= p1 + p2; i++)
 43     {
 44         pre[i] = i;
 45         r[i] = 0;
 46     }
 47 }
 48 int main()
 49 {
 50     while (~scanf("%d%d%d", &n, &p1, &p2))
 51     {
 52         if (n == 0 && p1 == 0 && p2 == 0) break;
 53         Init();
 54         char s[5];
 55         int a, b, k;
 56         for (int i = 0; i < n; i++)
 57         {
 58             scanf("%d%d%s", &a, &b, s);
 59             if (s[0] == 'y') k = 0;
 60             else k = 1;
 61             Union(a, b, k);
 62         }
 63         //处理完后,得到若干个集合,每个集合中两两同类或不同类
 64         //2:找到有多少个大集合,每个大集合中同类及不同类的个数
 65         memset(vis, 0, sizeof(vis));
 66         memset(cntnum, 0, sizeof(cntnum));
 67         memset(belong, 0, sizeof(belong));
 68 
 69         int cnt = 0;
 70         for (int i = 1; i <= p1 + p2; i++)
 71         {
 72             if (!vis[i])
 73             {
 74                 cnt++;
 75                 int f = Find(i);
 76                 id[f] = cnt;//对每个大集合的根进行分类
 77                 for (int j = i; j <= p1 + p2; j++)
 78                 {
 79                     if (Find(j) == f)
 80                     {
 81                         vis[j] = true;
 82                         cntnum[cnt][r[j]]++;
 83                         belong[j][0] = cnt;
 84                         belong[j][1] = r[j];
 85                     }
 86                 }
 87             }
 88         }
 89         //DP
 90         memset(dp, 0, sizeof(dp));
 91         dp[0][0] = 1;
 92         for (int i = 1; i <= cnt; i++)
 93         {
 94             for (int j = p1; j >= 0; j--)
 95             {
 96                 if (j - cntnum[i][0] >= 0 && dp[i - 1][j - cntnum[i][0]] > 0)
 97                 {
 98                     dp[i][j] += dp[i - 1][j - cntnum[i][0]];
 99                 }
100                 if (j - cntnum[i][1] >= 0 && dp[i - 1][j - cntnum[i][1]] > 0)
101                 {
102                     dp[i][j] += dp[i - 1][j - cntnum[i][1]];
103                 }
104             }
105         }
106 
107         //输出
108         if (dp[cnt][p1] != 1) printf("no\n");
109         else
110         {
111             int choose[maxn];//倒推找出选择的大集合的两类中是哪一类
112             int totalpeople = p1;
113             for (int i = cnt; i >= 1; --i)
114             {
115                 if (dp[i][totalpeople] == dp[i - 1][totalpeople - cntnum[i][0]])
116                 {
117                     choose[i] = 0;
118                     totalpeople -= cntnum[i][0];
119                 }
120                 else if (dp[i][totalpeople] == dp[i - 1][totalpeople - cntnum[i][1]])
121                 {
122                     choose[i] = 1;
123                     totalpeople -= cntnum[i][1];
124                 }
125             }
126 
127             for (int i = 1; i <= p1 + p2; i++)
128             {
129                 int fa = Find(i);
130                 int set_id = id[fa];
131                 if (belong[i][0] == set_id&&belong[i][1] == choose[set_id])
132                 {
133                     printf("%d\n", i);
134                 }
135             }
136             printf("end\n");
137         }
138     }
139     return 0;
140 }
View Code

7、poj 1456 Supermarket

  题意:有N件商品,分别给出商品的价值和销售的最后期限,只要在最后日期之前销售处,就能得到相应的利润,并且销售该商品需要1天时间。问销售的最大利润。

  思路:先把所有产品按照利润从大到小排序,假设一个产品a占用了一个日期后,那么如果下次又有一个产品b和产品a的截止日期是相同的,但是那个日期以被占用了,所以就要往前移动1天,那么就可以用并查集进行标记,在a占用了那个日期后,把a的截止日期指向前一个日期,这样的话,可以直接查找到他要占用到哪一个时间。

 1 #include<iostream>
 2 #include<algorithm>
 3 using namespace std;
 4 int n;
 5 const int maxn = 10010;
 6 const int maxt = 10010;
 7 struct node
 8 {
 9     int pi;
10     int di;
11 }goods[maxn];
12 
13 int pre[maxt];
14 int Find(int x)
15 {
16     int r = x;
17     while (r != pre[r])
18     {
19         r = pre[r];
20     }
21     int i = x, j;
22     while (pre[i] != r)
23     {
24         j = pre[i];
25         pre[i] = r;
26         i = j;
27     }
28     return r;
29 }
30 
31 void Union(int x, int y)
32 {
33     int fx = Find(x), fy = Find(y);
34     if (fx != fy)
35     {
36         if (fx < fy) pre[fy] = fx;
37         else pre[fx] = fy;
38     }
39 }
40 
41 
42 void Init()
43 {
44     for (int i = 0; i <maxt; i++)
45     {
46         pre[i] = i;
47     }
48 }
49 bool Cmp(const node&a, const node&b)
50 {
51     if (a.pi == b.pi)return a.di > b.di;
52     else return a.pi > b.pi;
53 }
54 int main()
55 {
56     while (~scanf("%d", &n))
57     {
58         Init();
59         for (int i = 0; i < n; i++)
60         {
61             scanf("%d%d", &goods[i].pi, &goods[i].di);
62         }
63         sort(goods, goods + n, Cmp);
64 
65         int sum = 0;
66         
67         for (int i = 0; i < n; i++)
68         {
69             int tt = goods[i].di;
70             int ff = Find(tt);
71             if (ff > 0)
72             {
73                 Union(ff, ff - 1);
74                 sum += goods[i].pi;
75             }
76         }
77         printf("%d\n", sum);
78     }
79     return 0;
80 }
View Code

8、poj 1703 Parity game

  题意:有一个长度已知的01串,给出[l,r]这个区间中的1是奇数个还是偶数个,给出一系列语句问前几个是正确的。

  思路:将[l,r]这个区间化为(l-1,r],那么1的个数就可以表示为sum[r]-sum[l-1],也就确定了奇偶性,我们可以用r[]数组表示这个端点到它的根节点的1的奇偶(这个区间就是(i,root(i)](0代表偶,1代表奇)  对于每个输入的区间,我们查找它们的根节点是否相同。

          ①如果相同,证明这个区间的奇偶性在之前已经得知,那么直接判断即可

          ②如果不同,那么就是u-1与v此时不在同一个集合中,则合并时更新r[].

 1 #include<iostream>
 2 #include<algorithm>
 3 using namespace std;
 4 int n, m;
 5 const int maxn = 5050;
 6 int pre[maxn];
 7 int val[maxn];
 8 struct node
 9 {
10     int ll;
11     int rr;
12     int d;
13 }qry[maxn];
14 int Map[maxn * 2];
15 int Find(int x)
16 {
17     if (x == pre[x]) return x;
18     else
19     {
20         int fa = pre[x];
21         pre[x] = Find(fa);
22         val[x] = (val[x] + val[fa]) % 2;
23         return pre[x];
24     }
25 }
26 bool Union(int x, int y, int v)
27 {//x<y
28     int rx = Find(x), ry = Find(y);
29     if (rx == ry) return true;
30     else
31     {
32         if (rx < ry)
33         {
34             pre[ry] = rx;
35             val[ry] = (val[x] + v - val[y] + 2) % 2;
36         }
37         else
38         {
39             pre[rx] = ry;
40             val[rx] = (val[y] - v - val[x] + 2) % 2;
41         }
42         return false;
43     }
44 }
45 int main()
46 {
47     while (~scanf("%d", &n))
48     {
49         scanf("%d", &m);
50         int l, r, v;
51         char s[6];
52         int cnt = 0;
53         for (int i = 0; i < m; i++)
54         {
55             scanf("%d%d%s", &l, &r, s);
56             l = l - 1;
57             if (s[0] == 'e') v = 0;
58             else v = 1;
59             qry[i].ll = l, qry[i].rr = r, qry[i].d = v;
60             Map[cnt++] = l;
61             Map[cnt++] = r;
62         }
63         //离散化
64         sort(Map, Map + cnt);
65         cnt = unique(Map, Map + cnt) - Map;
66         for (int i = 0; i <= cnt; i++)
67         {
68             pre[i] = i;
69             val[i] = 0;
70         }
71         int ans = 0;
72         for (int i = 0; i < m; i++)
73         {
74             l = lower_bound(Map, Map + cnt, qry[i].ll) - Map;
75             r = lower_bound(Map, Map + cnt, qry[i].rr) - Map;
76             v = qry[i].d;
77             if (Union(l, r, v))
78             {
79                 if ((val[r] - val[l] + 2) % 2 != v)
80                 {
81                     break;
82                 }
83 
84             }
85             ans++;
86         }
87         printf("%d\n", ans);
88     }
89     return 0;
90 }
View Code

9、poj 1984 Navigation Nightmare

  题意:图上有一些村庄,村庄之间用只能指向东南西北的方向的道路连接,村庄只能位于道路的两端。询问截止到第I个已知条件时某两个村庄之间的曼哈顿距离。

  思路:先根据道路设出每个点的坐标,然后如果某个已知条件已知,则合并。询问时判断两个村庄是否在同一个连通块即可。

  1 #include<iostream>
  2 #include<algorithm>
  3 #include<vector>
  4 using namespace std;
  5 int n, m,k;
  6 const int maxn = 40050;
  7 const int maxm = 40050;
  8 const int maxk = 10050;
  9 struct qry
 10 {
 11     int t;
 12     int f1;
 13     int f2;
 14     int id;
 15 }qrys[maxk];
 16 int ans[maxk];
 17 
 18 struct stp
 19 {
 20     int f1;
 21     int f2;
 22     stp(int ff1 = 0, int ff2 = 0) :f1(ff1), f2(ff2)
 23     {
 24     }
 25 };
 26 vector<stp>stps;
 27 struct pt
 28 {
 29     int x;
 30     int y;
 31 }points[maxn];
 32 bool Cmp1(const qry&a, const qry&b)
 33 {
 34     return a.t < b.t;
 35 }
 36 
 37 int pre[maxn];
 38 int Find(int x)
 39 {
 40     if (pre[x] == x) return x;
 41     else
 42     {
 43         int fa = pre[x];
 44         pre[x] = Find(fa);
 45         return pre[x];
 46     }
 47 }
 48 bool Union(int x, int y)
 49 {
 50     int fx = Find(x), fy = Find(y);
 51     if (fx == fy) return true;
 52     else
 53     {
 54         pre[fx] = fy;
 55         return false;
 56     }
 57 }
 58 struct node
 59 {
 60     int to;
 61     char dir;
 62     int len;
 63     node(int tt=0,char c=0,int ll=0):to(tt),dir(c),len(ll){ }
 64 };
 65 vector<node>mp[maxn];
 66 bool vis[maxn];
 67 void DFS(int r)
 68 {
 69 
 70     int sz = mp[r].size();
 71     for (int i = 0; i < sz; i++)
 72     {
 73         int tx = points[r].x, ty = points[r].y;
 74         int v = mp[r][i].to, length = mp[r][i].len;
 75         if (vis[v])continue;
 76         switch (mp[r][i].dir)
 77         {
 78         case 'N':tx -= length;
 79             break;
 80         case 'E': ty += length;
 81             break;
 82         case 'S':tx += length;
 83             break;
 84         case 'W':ty -= length;
 85             break;
 86         }
 87         points[v].x = tx, points[v].y = ty;
 88         vis[v] = true;
 89         DFS(v);
 90     }
 91 }
 92 
 93 int main()
 94 {
 95     while (~scanf("%d%d", &n, &m))
 96     {
 97         for (int i = 0; i <= n; i++) mp[i].clear();
 98         stps.clear();
 99         for (int i = 1; i <= m; i++)
100         {
101             int u, v, l;
102             char op[10];
103             scanf("%d%d%d%s", &u, &v, &l,op);
104             mp[u].push_back(node(v, op[0], l));
105             char nc;
106             switch (op[0])
107             {
108             case 'N':
109                 nc = 'S';
110                 break;
111             case 'S':
112                 nc = 'N';
113                 break;
114             case 'E':
115                 nc = 'W';
116                 break;
117             case 'W':
118                 nc = 'E';
119                 break;
120             }
121             mp[v].push_back(node(u, nc, l));
122             stps.push_back(stp(u, v));
123         }
124         memset(vis, 0, sizeof(vis));
125         for (int i = 0; i <= n; i++)
126         {
127             if (mp[i].size())
128             {
129                 points[i].x = 0, points[i].y = 0;
130                 vis[i] = true;
131                 DFS(i);
132                 break;
133             }
134         }
135         scanf("%d", &k);
136         for (int i = 0; i < k; i++)
137         {
138             scanf("%d%d%d",&qrys[i].f1, &qrys[i].f2, &qrys[i].t);
139             qrys[i].id = i + 1;
140         }
141         sort(qrys, qrys + k, Cmp1);
142 
143         for (int i = 0; i <= n; i++) pre[i] = i;
144         int nowstep = 0;
145         for (int i = 0; i < k; i++)
146         {
147             int t = qrys[i].t;
148             for (; nowstep < t; nowstep++)
149             {
150                 int f1 = stps[nowstep].f1;
151                 int f2 = stps[nowstep].f2;
152                 Union(f1, f2);
153             }
154             int x = qrys[i].f1, y = qrys[i].f2;
155             int fx = Find(x), fy = Find(y);
156             if (fx != fy)
157             {
158                 ans[qrys[i].id] = -1;
159             }
160             else ans[qrys[i].id] = abs(points[x].x - points[y].x) + abs(points[x].y - points[y].y);
161         }
162         for (int i = 1; i <= k; i++)
163         {
164             printf("%d\n", ans[i]);
165         }
166     }
167     return 0;
168 }
View Code

10、hdu 1829 A Bug's Life

  题意:给定一系列数对,例如a和b,表示a和b不是同一种性别,然后不断的给出这样的数对,问有没有矛盾的情况。

  思路:带权并查集。

 1 #include<iostream>
 2 using namespace std;
 3 const int maxn = 2100;
 4 int pre[maxn];
 5 int r[maxn];
 6 int n, m;
 7 void Init()
 8 {
 9     for (int i = 0; i <= n; i++)
10     {
11         pre[i] = i;
12         r[i] = 0;
13     }
14 }
15 int Find(int x)
16 {
17     if (pre[x] == x)return x;
18     else
19     {
20         int fa = pre[x];
21         pre[x] = Find(fa);
22         r[x] = r[x] ^ r[fa];
23         return pre[x];
24     }
25 }
26 
27 bool Join(int x, int y)
28 {
29     int fx = Find(x), fy = Find(y);
30     if (fx == fy)return true;
31     else
32     {
33         pre[fx] = fy;
34         r[fx] = r[x] ^ r[y] ^ 1;
35         return false;
36     }
37 }
38 int main()
39 {
40     int t;
41     int Case = 1;
42     scanf("%d", &t);
43     while (t--)
44     {
45         scanf("%d%d", &n, &m);
46         Init();
47         bool flag = true;
48         while (m--)
49         {
50             int u, v;
51             scanf("%d%d", &u, &v);
52             if (!flag)continue;
53             if (Join(u, v))
54             {
55                 if (r[u] ^ r[v] != 1)
56                 {
57                     flag = false;
58                 }
59             }
60         }
61         if (flag) printf("Scenario #%d:\nNo suspicious bugs found!\n\n", Case++);
62         else printf("Scenario #%d:\nSuspicious bugs found!\n\n", Case++);
63     }
64     return 0;
65 }
View Code

11、poj 2912 Rochambeau

  题意:有N个人玩剪刀石头布,其中有个人是裁判,其他人分为3组。 这3组中每个组分别出剪刀,石头和布。 裁判可以任意出3个中的一个。 现在有M个回合,每个回合从N个人中任意选出两个人来玩,并给出结果。 要求输出最早找出裁判的回合数,以及裁判的编号。还有可能无法确定。

  思路:利用并查集建立起相对关系,枚举裁判是哪一个,对有他参与的回合不予处理,如果在他不参与的回合中出现了矛盾,那么这个人一定就不是裁判。

 1 #include<iostream>
 2 using namespace std;
 3 const int maxn = 510;
 4 const int maxm = 2010;
 5 int n, m;
 6 int pre[maxn];
 7 int r[maxn];//0:平局;1:pre[x]赢了x;2:x赢了pre[x]
 8 void Init()
 9 {
10     for (int i = 0; i <= n; i++)
11     {
12         pre[i] = i;
13         r[i] = 0;
14     }
15 }
16 struct stp
17 {
18     int u;
19     int v;
20     int d;
21 }stps[maxm];
22 
23 int ok[maxn];
24 
25 int Find(int x)
26 {
27     if (pre[x] == x)return x;
28     else
29     {
30         int fa = pre[x];
31         pre[x] = Find(fa);
32         r[x] = (r[x] + r[fa]) % 3;
33         return pre[x];
34     }
35 }
36 bool Union(int x, int y,int d)
37 {
38     int fx = Find(x), fy = Find(y);
39     if (fx == fy)return true;
40     else
41     {
42         pre[fx] = fy;
43         r[fx] = (3 - r[x] + d + r[y]) % 3;
44         return false;
45     }
46 }
47 int main()
48 {
49     while (~scanf("%d%d", &n, &m))
50     {
51         for (int i = 0; i < m; i++)
52         {
53             int u, v,d;
54             char c;
55             scanf("%d%c%d", &u, &c, &v);
56             if (c == '=')d = 0;
57             else if (c == '<') d = 1;
58             else d = 2;
59             stps[i].u = u, stps[i].v=v, stps[i].d=d;
60         }
61         memset(ok, 0, sizeof(ok));
62         for (int i = 0; i < n; i++)
63         {
64             Init();
65             for (int j = 0; j < m; j++)
66             {
67                 if (stps[j].u == i || stps[j].v == i)continue;
68                 if (Union(stps[j].u, stps[j].v, stps[j].d))
69                 {
70                     if ((r[stps[j].u]+3-r[stps[j].v]) % 3 != stps[j].d)
71                     {
72                         ok[i] = j + 1;
73                         break;
74                     }
75                 }
76             }
77         }
78         int cnt = 0,rline=0,judgeperson=0;
79         for (int i = 0; i < n; i++)
80         {
81             if (ok[i]== 0)
82             {
83                 cnt++;
84                 judgeperson = i;
85             }
86             if (ok[i] > rline) rline = ok[i];
87         }
88         if (cnt == 0)printf("Impossible\n");
89         else if (cnt == 1)printf("Player %d can be determined to be the judge after %d lines\n",judgeperson,rline);
90         else printf("Can not determine\n");
91     }
92     return 0;
93 }
View Code

12、hdu 1272 小希的迷宫

  题意:如果有一个通道连通了房间A和B,那么既可以通过它从房间A走到房间B,也可以通过它从房间B走到房间A,为了提高难度,小希希望任意两个房间有且仅有一条路径可以相通(除非走了回头路)。小希现在把她的设计图给你,让你帮忙判断她的设计图是否符合她的设计思路。

  思路:并查集。

 1 #include<algorithm>
 2 using namespace std;
 3 const int maxn = 100010;
 4 int pre[maxn];
 5 bool vis[maxn];
 6 int Find(int x)
 7 {
 8     if (pre[x] == x)return x;
 9     else
10     {
11         int fa = pre[x];
12         pre[x] = Find(fa);
13         return pre[x];
14     }
15 }
16 
17 bool Union(int x, int y)
18 {
19     int fx = Find(x), fy = Find(y);
20     if (fx == fy)return true;
21     else
22     {
23         pre[fx] = fy;
24         return false;
25     }
26 }
27 int main()
28 {
29     int u, v;
30     while (~scanf("%d%d", &u, &v))
31     {
32         if (u == -1 && v == -1)break;
33         if (u == 0 && v == 0)
34         {
35             printf("Yes\n");
36             continue;
37         }
38         for (int i = 0; i < maxn; i++)pre[i] = i;
39         bool flag = true;
40         do
41         {
42             if (!flag)continue;
43             if (Union(u, v))
44             {
45                 flag = false;
46             }
47         } while (scanf("%d%d", &u, &v) && u != 0 && v != 0);
48         if (!flag) printf("No\n");
49         else
50         {
51             memset(vis, 0, sizeof(vis));
52             int cnt = 0;
53             for (int i = 0; i < maxn; i++)
54             {
55                 if (pre[i] == i)continue;
56                 int f = Find(i);
57                 if (!vis[f])
58                 {
59                     cnt++;
60                     vis[f] = true;
61                 }
62             }
63             if (cnt == 1) printf("Yes\n");//只能有一个连通块
64             else printf("No\n");
65         }
66     }
67     return 0;
68 }
View Code

13、poj 1308 Is It A Tree

  题意:输入数据表示存在父子节点关系的点的编号,判断是否为树。

  思路:并查集。

 1 #include<algorithm>
 2 using namespace std;
 3 const int maxn = 100010;
 4 int pre[maxn];
 5 bool vis[maxn];
 6 int Find(int x)
 7 {
 8     if (pre[x] == x)return x;
 9     else
10     {
11         int fa = pre[x];
12         pre[x] = Find(fa);
13         return pre[x];
14     }
15 }
16 
17 bool Union(int x, int y)
18 {
19     int fx = Find(x), fy = Find(y);
20     if (fx == fy)return true;
21     else
22     {
23         pre[fx] = fy;
24         return false;
25     }
26 }
27 int main()
28 {
29     int u, v;
30     int Case = 1;
31     while (~scanf("%d%d", &u, &v))
32     {
33         if (u<0 && v <0)break;
34         if (u == 0 && v == 0)
35         {
36             printf("Case %d is a tree.\n",Case++);
37             continue;
38         }
39         for (int i = 0; i < maxn; i++)pre[i] = i;
40         bool flag = true;
41         do
42         {
43             if (!flag)continue;
44             if (Union(u, v))
45             {
46                 flag = false;
47             }
48         } while (scanf("%d%d", &u, &v) && u != 0 && v != 0);
49         if (!flag) printf("Case %d is not a tree.\n",Case++);
50         else
51         {
52             memset(vis, 0, sizeof(vis));
53             int cnt = 0;
54             for (int i = 0; i < maxn; i++)
55             {
56                 if (pre[i] == i)continue;
57                 int f = Find(i);
58                 if (!vis[f])
59                 {
60                     cnt++;
61                     vis[f] = true;
62                 }
63             }
64             if (cnt == 1) printf("Case %d is a tree.\n", Case++);//只能有一个连通块
65             else printf("Case %d is not a tree.\n",Case++);
66         }
67     }
68     return 0;
69 }
View Code

 14、LA 3644 X-Plosives

  题意:给出若干对,表示a和b相连,要保证每个连通块为一颗树,要删去哪些对。

  思路:并查集。

 1 #include<iostream>
 2 using namespace std;
 3 const int maxn = 100100;
 4 int pre[maxn];
 5 int Find(int x)
 6 {
 7     if (x == pre[x]) return x;
 8     else
 9     {
10         int fa = pre[x];
11         pre[x] = Find(fa);
12         return pre[x];
13     }
14 }
15 bool Union(int x, int y)
16 {
17     int fx = Find(x), fy = Find(y);
18     if (fx == fy) return true;
19     else
20     {
21         pre[fx] = fy;
22         return false;
23     }
24 }
25 int main()
26 {
27     int a,b;
28     while (~scanf("%d", &a))
29     {
30         scanf("%d", &b);
31         for (int i = 0; i < maxn; i++) pre[i] = i;
32         Union(a, b);
33         int cnt = 0;
34         while (scanf("%d", &a) && a != -1)
35         {
36             scanf("%d", &b);
37             if (Union(a, b)) cnt++;
38         }
39         printf("%d\n", cnt);
40     }
41     return 0;
42 }
View Code

 15、LA 3027 Corporative Network

  题意:每次可以询问一家公司到其所在的群的中心公司的线路长度,或者将某个群的中心公司A接到另一个群的某家公司B,并且两个群所有的公司的中心改为原来B所在群的中心公司。

  思路:带权并查集。

 1 #include<iostream>
 2 #include<cmath>
 3 #include<cstdio>
 4 using namespace std;
 5 const int maxn = 20010;
 6 int pre[maxn];
 7 int val[maxn];
 8 int n;
 9 void Init()
10 {
11     for (int i = 0; i <= n; i++) pre[i] = i, val[i] = 0;
12 }
13 int Find(int x)
14 {
15     if (x == pre[x]) return x;
16     else
17     {
18         int fa = pre[x];
19         pre[x] = Find(fa);
20         val[x] = val[x] + val[fa];
21         return pre[x];
22     }
23 }
24 bool Union(int x, int y,int l)
25 {//I x y
26     int fx = Find(x), fy = Find(y);
27     if (fx == fy) return true;
28     else
29     {
30         pre[fx] = fy;
31         val[fx] = l + val[y];
32         return false;
33     }
34 }
35 int main()
36 {
37     int t;
38     scanf("%d", &t);
39     char s[5];
40     while (t--)
41     {
42         scanf("%d", &n);
43         Init();
44         while (~scanf("%s", s))
45         {
46             if (s[0] == 'O')break;
47             if (s[0] == 'E')
48             {
49                 int x;
50                 scanf("%d", &x);
51                 Find(x);
52                 printf("%d\n", val[x]);
53             }
54             else
55             {
56                 int x, y;
57                 scanf("%d%d", &x, &y);
58                 Union(x, y, abs(x - y) % 1000);
59             }
60         }
61     }
62     return 0;
63 }
View Code

 16、LA 4487 Exclusive-OR

  题意:每次告诉你A的值是多少或者A和B异或的结果是多少,或者询问k个数连续异或的值。

  思路:带权并查集。

  1 #include <iostream>
  2 #include <cstdio>
  3 #include <cstring>
  4 using namespace std;
  5 const int maxn = 21000;
  6 int pre[maxn], re[maxn];
  7 int vis[maxn], num[20];
  8 int supperroot = maxn - 10;
  9 void init(int n)
 10 {
 11     for (int i = 0; i <= n + 1; i++)
 12     {
 13         pre[i] = i;
 14         re[i] = 0;
 15         vis[i] = 0;
 16     }
 17     pre[supperroot] = supperroot;
 18     re[supperroot] = 0;
 19 }
 20 int Find(int x)
 21 {
 22     if (x != pre[x])
 23     {
 24         int fa = pre[x];
 25         pre[x] = Find(pre[x]);
 26         re[x] ^= re[fa];
 27     }
 28     return pre[x];
 29 }
 30 bool Union(int x, int y, int val)
 31 {
 32     int fx = Find(x);
 33     int fy = Find(y);
 34     if (fx == fy)
 35     {
 36         if ((re[x] ^ re[y]) != val) return 0;
 37         else return 1;
 38     }
 39     if (fx == supperroot) swap(fx, fy);
 40     pre[fx] = fy;
 41     re[fx] = re[x] ^ re[y] ^ val;
 42     return 1;
 43 }
 44 int main()
 45 {
 46     int n, Q, p, q, val, kase = 0;
 47     char ch[2];
 48     char s[40];
 49     while (scanf("%d%d", &n, &Q))
 50     {
 51         if (n==0&&Q==0) break;
 52         init(n);
 53         printf("Case %d:\n", ++kase);
 54         bool flag = 0;
 55         int facts = 0;
 56         for (int i = 1; i <= Q; i++)
 57         {
 58             scanf("%s", ch);
 59             if (ch[0] == 'I')
 60             {
 61                 facts++;
 62                 gets(s);
 63                 if (sscanf(s, "%d%d%d", &p, &q, &val) == 2)
 64                 {
 65                     val = q; 
 66                     q = supperroot;
 67                 }
 68                 if (flag) continue;
 69                 if (!Union(p, q, val))
 70                 {
 71                     printf("The first %d facts are conflicting.\n", facts);
 72                     flag = 1;
 73                 }
 74             }
 75             else
 76             {
 77                 int k, ans = 0;
 78                 scanf("%d", &k);
 79                 for (int i = 1; i <= k; i++)
 80                 {
 81                     scanf("%d", &num[i]);
 82                     if (flag) continue;
 83                     int fa = Find(num[i]);
 84                     ans ^= re[num[i]];
 85                     num[i] = fa;
 86                     vis[fa] ^= 1;
 87                 }
 88                 if (flag) continue;
 89                 bool unknow = false;
 90                 for (int i = 1; i <= k; i++)
 91                 {
 92                     if (vis[num[i]])
 93                     {
 94                         if (num[i] != supperroot)
 95                         {
 96                             unknow = true;
 97                         }
 98                         vis[num[i]] = 0;
 99                     }
100                 }
101                 if (!unknow) printf("%d\n", ans);
102                 else printf("I don't know.\n");
103             }
104         }
105         printf("\n");
106     }
107     return 0;
108 }
View Code

 17、uva 11987 Almost Union-Find

  题意:第一种操作为把P和Q所在集合和并,第二种为把结点P放到Q所在集合里,第三种是查询P所在集合的元素数目和总和。

  思路:带权并查集。初始时把每个结点的祖先指向i+n,这样当第二种操作时,就不会影响P原来集合的根。

 1 #include<iostream>
 2 using namespace std;
 3 const int maxn = 100100;
 4 int pre[maxn];
 5 int sum[maxn];
 6 int cnt[maxn];
 7 int n, m;
 8 int Find(int x)
 9 {
10     if (x == pre[x]) return x;
11     else
12     {
13         int fa = pre[x];
14         pre[x] = Find(fa);
15         return pre[x];
16     }
17 }
18 void Union(int x, int y)
19 {
20     int fx = Find(x), fy = Find(y);
21     if (fx != fy)
22     {
23         pre[fx] = fy;
24         sum[fy] += sum[fx];
25         cnt[fy] += cnt[fx];
26         sum[fx] = 0;
27         cnt[fx] = 0;
28     }
29 }
30 void Change(int x, int to)
31 {
32     int f = Find(to);
33     int init = Find(x);
34     pre[x] = f;
35     sum[f] += x, cnt[f]++;
36     sum[init] -= x, cnt[init]--;
37 }
38 int main()
39 {
40     while (~scanf("%d%d", &n, &m))
41     {
42         for (int i = 0; i <= n; i++) pre[i] = i+n, cnt[i+n] = 1, sum[i+n] = i,pre[i+n]=i+n;
43         int s;
44         while (m--)
45         {
46             scanf("%d", &s);
47             if (s == 1)
48             {
49                 int p, q;
50                 scanf("%d%d", &p, &q);
51                 Union(p, q);
52             }
53             else if (s == 2)
54             {
55                 int p, q;
56                 scanf("%d%d", &p, &q);
57                 Change(p, q);
58             }
59             else
60             {
61                 int p;
62                 scanf("%d", &p);
63                 printf("%d %d\n", cnt[Find(p)], sum[Find(p)]);
64             }
65         }
66     }
67     return 0;
68 }
View Code

 18、LA 5898/uva 1493/hdu 4056 Draw a Mess

  题意:有若干个人按照顺序在n*m的黑板上画4种图形。求最后各种颜色所占的像素数目。

  思路:用并查集维护每行当前位置的下一个可以画的位置,然后倒序更新。

 1 #include<cmath>
 2 #include<cstdio>
 3 #include<algorithm>
 4 #include<iostream>
 5 #include<cstring>
 6 using namespace std;
 7 
 8 const int maxn = 205;
 9 const int maxm=100500;
10 char str[maxm][25];
11 int xx[maxm], yy[maxm], p[maxm], t[maxm], c[maxm];
12 int Next[maxn][maxm];
13 int Find(int i, int t)
14 {
15     if (Next[i][t] != t)
16         Next[i][t] = Find(i, Next[i][t]);
17     return Next[i][t];
18 }
19 int dele(int i, int l, int r)
20 {
21     int ret = 0;
22     l = Find(i, l);
23     while (l <= r)
24     {
25         ret++;
26         Next[i][l] = l + 1;
27         l = Find(i, l);
28     }
29     return ret;
30 }
31 int main()
32 {
33     int n, m, q;
34     int i, j, x, y, r, w, l, h, color;
35     int tmp1, tmp2, tmp3;
36     int ans[10];
37     while (~scanf("%d%d%d", &n, &m, &q))
38     {
39         for (i = 0; i<n; i++)
40             for (j = 0; j <= m; j++)
41                 Next[i][j] = j;
42         memset(ans, 0, sizeof(ans));
43         for (i = 0; i<q; i++)
44         {
45             scanf("%s%d%d%d%d", str[i], &xx[i], &yy[i], &tmp1, &tmp2);
46             if (str[i][0] != 'R')
47                 p[i] = tmp1, c[i] = tmp2;
48             else
49             {
50                 scanf("%d", &tmp3);
51                 p[i] = tmp1; t[i] = tmp2; c[i] = tmp3;
52             }
53         }
54         for (i = q - 1; i >= 0; i--)
55         {
56             x = xx[i], y = yy[i];
57             if (str[i][0] == 'D')
58             {//菱形
59                 r = p[i], color = c[i];
60                 for (j = max(0, x - r); j <= min(n - 1, x + r); j++)//枚举行
61                     ans[color] += dele(j, max(0, y - r + abs(x - j)),min(m - 1, y + r - abs(x - j)));
62             }
63             else if (str[i][0] == 'T')
64             {//等腰三角形
65                 w = p[i], color = c[i];
66                 h = (w + 1) / 2;
67                 for (j = max(0, x); j<min(n, x + h); j++)
68                     ans[color] += dele(j,max(0, y - h + j - x + 1),min(m - 1, y + h - j + x - 1));
69             }
70             else if (str[i][0] == 'R')
71             {//矩形
72                 l = p[i], w = t[i], color = c[i];
73                 for (j = max(0, x); j <= min(n - 1, x + l - 1); j++)
74                     ans[color] += dele(j, y,min(m - 1, y + w - 1));
75             }
76             else if (str[i][0] == 'C')
77             {//圆形
78                 r = p[i]; color = c[i];
79                 for (j = max(0, x - r); j <= min(n - 1, x + r); j++)
80                 {
81                     int tmp = floor(sqrt(1ll * r*r - (j - x)*(j - x)));
82                     ans[color] += dele(j, max(0, y - tmp), min(m - 1, y + tmp));
83                 }
84             }
85         }
86         for (i = 1; i <= 9; i++)
87         {
88             if (i > 1) printf(" ");
89             printf("%d", ans[i]);
90         }
91         printf("\n");
92     }
93     return 0;
94 }
View Code

 19、OpenJ_POJ - C15C  Rabbit's Festival

  题意:每个结点都住着一只兔子,一共有n个结点,有m条无向边,每条边在第ci天时无法通行。问从1~k天中,每天可以两两相遇的兔子的对数?

  思路:某一天可以相遇,则在同一个并查集当中,易得对数。怎么维护这个并查集呢?CDQ分治。参考:http://www.bubuko.com/infodetail-1220058.html

 

  1 //CDQ分治+并查集
  2 #include<cstdio>
  3 #include<algorithm>
  4 #include<cstring>
  5 using namespace std;
  6 const int maxn = 100010;
  7 const int maxe = 200010;
  8 struct edge
  9 {
 10     int from, to,next;
 11     edge(int ff=0,int tt=0,int nn=0):from(ff),to(tt),next(nn){}
 12 }Edge[maxe];
 13 int Head[maxn], totedge;
 14 int pre[maxn], cnt[maxn];//其父结点、以其为根的树的结点个数
 15 int n, m, k;
 16 void addedge(int from, int to, int c)
 17 {
 18     Edge[totedge] = edge(from, to, Head[c]);
 19     Head[c] = totedge++;
 20 }
 21 void init()
 22 {
 23     for (int i = 1; i <= n; i++) pre[i] = i, cnt[i] = 1;
 24     memset(Head, -1, sizeof(Head));
 25     totedge = 0;
 26 }
 27 int find(int x)
 28 {//找到根结点,不用路径压缩
 29     while (pre[x] != x)
 30     {
 31         x = pre[x];
 32     }
 33     return x;
 34 }
 35 long long Cal(int x)
 36 {//以x为根的所有结点可以形成的配对对数
 37     return 1ll * x*(x - 1) / 2;
 38 }
 39 struct CDQ
 40 {
 41     long long result;
 42     pair<int, int> stk[maxe];//保存最近添加的边,方便从并查集删去
 43     int stkTop;
 44     void Push(edge&cur)
 45     {//将边cur加入并查集
 46         int a = find(cur.from);
 47         int b = find(cur.to);
 48         if (a == b) return;
 49         result -= Cal(cnt[a]);
 50         result -= Cal(cnt[b]);
 51         if (cnt[a] > cnt[b]) swap(a, b);
 52         cnt[b] += cnt[a];
 53         pre[a] = b;
 54         result += Cal(cnt[b]);
 55 
 56         stk[++stkTop] = make_pair(a, b);
 57     }
 58     void Push(int l, int r)
 59     {//从l~r天所有不可通的道路加入并查集(即当前遍历的天数不在l~r中)
 60         for (int i = l; i <= r; i++)
 61         {
 62             for (int j = Head[i]; j != -1; j = Edge[j].next)
 63             {
 64                 Push(Edge[j]);
 65             }
 66         }
 67     }
 68     void Pop(int l, int r)
 69     {
 70         for (int i = r; i >= l; i--)
 71         {
 72             int u = stk[i].first;
 73             int v = stk[i].second;
 74             result -= Cal(cnt[v]);
 75             cnt[v] -= cnt[u];
 76             result += Cal(cnt[u]);
 77             result += Cal(cnt[v]);
 78             pre[u] = u;
 79         }
 80     }
 81 
 82     void cdq(int l, int r)
 83     {//将从l~r天可以连通的道路加入并查集,不可连通的则从并查集删去
 84         if (l == r)
 85         {//如果刚好第l天没有连通的道路不在并查集中
 86             printf("%lld\n", result);
 87             return;
 88         }
 89         int mid = (l + r) >> 1;
 90         int tmp = stkTop;
 91         Push(mid + 1, r);//考虑l~mid天,将第mid+1~r天不可连通的道路加入并查集
 92         cdq(l, mid);
 93         Pop(tmp + 1, stkTop);//把加入的边删去
 94         stkTop = tmp;
 95         Push(l,mid);//考虑mid+1~r天,将第l~mid天不可连通的道路加入并查集
 96         cdq(mid + 1, r);
 97         Pop(tmp + 1, stkTop);
 98         stkTop = tmp;
 99     }
100     void cdqinit()
101     {
102         stkTop = -1;
103         result = 0;
104     }
105     void Run()
106     {
107         cdq(1, k);
108     }
109 }CDQ;
110 int main()
111 {
112     while (~scanf("%d%d%d", &n, &m, &k))
113     {
114         init();
115         CDQ.cdqinit();
116         for (int i = 1; i <= m; i++)
117         {
118             int u, v, c;
119             scanf("%d%d%d", &u, &v, &c);
120             if (c <= k)
121             {//如果1~k中有某一天不通,则加到对应边中
122                 addedge(u, v, c);
123                 continue;
124             }
125             //将从1~k天都能连通的路直接连起来
126             edge tmp = edge(u, v, 0);
127             CDQ.Push(tmp);
128         }
129         CDQ.Run();
130     }
131     return 0;
132 }
View Code

 

posted @ 2017-08-25 15:43  萌萌的美男子  阅读(338)  评论(0编辑  收藏  举报