1. 合根植物

w星球的一个种植园,被分成 m * n 个小格子(东西方向m行,南北方向n列)。每个格子里种了一株合根植物。
  这种植物有个特点,它的根可能会沿着南北或东西方向伸展,从而与另一个格子的植物合成为一体。

如果我们告诉你哪些小格子间出现了连根现象,你能说出这个园中一共有多少株合根植物吗?

输入格式
第一行,两个整数m,n,用空格分开,表示格子的行数、列数(1<m,n<1000)。
  接下来一行,一个整数k,表示下面还有k行数据(0<k<100000)
  接下来k行,第行两个整数a,b,表示编号为a的小格子和编号为b的小格子合根了。

格子的编号一行一行,从上到下,从左到右编号。
  比如:5 * 4 的小格子,编号:
  1 2 3 4
  5 6 7 8
  9 10 11 12
  13 14 15 16
  17 18 19 20
样例输入
5 4
16
2 3
1 5
5 9
4 8
7 8
9 10
10 11
11 12
10 14
12 16
14 18
17 18
15 19
19 20
9 13
13 17
样例输出
5
样例说明
  其合根情况参考下图

思路: 很简单的并查集题目。

 1 #include <iostream>
 2 
 3 using namespace std;
 4 int m,n,sum;
 5 int pre[1000005];
 6 void init()
 7 {
 8     for(int i=1;i<=sum;i++)
 9         pre[i] = i;
10 }
11 int find_pre(int x)
12 {
13     if(x==pre[x])
14         return x;
15     else
16         return pre[x] = find_pre(pre[x]);
17 }
18 bool Union(int x,int y)
19 {
20     int rootx = find_pre(x),rooty = find_pre(y);
21     if(rootx != rooty)
22     {
23         pre[rootx] = rooty;
24         return true;
25     }
26     return false;
27 }
28 int main()
29 {
30     int k,a,b;
31     scanf("%d %d",&m,&n);
32     sum = m*n;
33     scanf("%d",&k);
34     init();
35     for(int i=0;i<k;i++)
36     {
37         scanf("%d %d",&a,&b);
38         if(Union(a,b))
39             sum--;
40     }
41     printf("%d\n",sum);
42     return 0;
43 }

 

 

2. 国王的烦恼

C国由n个小岛组成,为了方便小岛之间联络,C国在小岛间建立了m座大桥,每座大桥连接两座小岛。两个小岛间可能存在多座桥连接。然而,由于海水冲刷,有一些大桥面临着不能使用的危险。
  如果两个小岛间的所有大桥都不能使用,则这两座小岛就不能直接到达了。然而,只要这两座小岛的居民能通过其他的桥或者其他的小岛互相到达,他们就会安然无事。但是,如果前一天两个小岛之间还有方法可以到达,后一天却不能到达了,居民们就会一起抗议。
  现在C国的国王已经知道了每座桥能使用的天数,超过这个天数就不能使用了。现在他想知道居民们会有多少天进行抗议。

输入格式

输入的第一行包含两个整数n, m,分别表示小岛的个数和桥的数量。
  接下来m行,每行三个整数a, b, t,分别表示该座桥连接a号和b号两个小岛,能使用t天。小岛的编号从1开始递增。

输出格式

输出一个整数,表示居民们会抗议的天数。

样例输入

4 4
1 2 2
1 3 2
2 3 1
3 4 3

样例输出

2

样例说明

第一天后2和3之间的桥不能使用,不影响。
  第二天后1和2之间,以及1和3之间的桥不能使用,居民们会抗议。
  第三天后3和4之间的桥不能使用,居民们会抗议。

数据规模和约定

对于30%的数据,1<=n<=20,1<=m<=100;
  对于50%的数据,1<=n<=500,1<=m<=10000;
  对于100%的数据,1<=n<=10000,1<=m<=100000,1<=a, b<=n, 1<=t<=100000。

 思路:桥坏的过程是一个单集合转化为多集合的过程,这个过程和并查集刚好相反,所以这道题需要逆推,将桥坏的过程转化为建桥的过程。首先设定一个数据结构,保存桥和桥坏的时间,然后按照桥坏的时间给这个结构体排序(倒叙)。如果桥建立以后,两座岛由两个集合合并为一个集合,则等价于这座桥坏了以后,两座岛不再在一个集合内,这个时候居民就要抗议。

 1 #include<iostream>
 2 #include<algorithm>
 3 using namespace std;
 4 struct line{
 5     int a,b,t;
 6     line(int a,int b,int t):a(a),b(b),t(t){}
 7     line(){}
 8     bool operator<(const line&b)
 9     {
10         return t>b.t;
11     }
12 }s[100005];
13 int pre[10005];
14 int sum = 0,n,m;
15 void init()
16 {
17     for(int i=1;i<=n;i++)
18         pre[i] = i;
19 }
20 int find_pre(int x)
21 {
22     if(x==pre[x])
23         return x;
24     else
25         return pre[x] = find_pre(pre[x]);
26 }
27 bool Union(int x,int y)
28 {
29     int rootx = find_pre(x),rooty = find_pre(y);
30     if(rootx != rooty)
31     {
32         pre[rootx] = rooty;
33         return true;
34     }
35     return false;
36 }
37 int main()
38 {
39     scanf("%d %d",&n,&m);
40     init();
41     int a,b,t;
42     for(int i=1;i<=m;i++)
43     {
44         scanf("%d %d %d",&a,&b,&t);
45         s[i] = line(a,b,t);
46     }
47     sort(s+1,s+n+1);
48     int lastday = 0;
49     for(int i=1;i<=m;i++)
50     {
51         if(Union(s[i].a,s[i].b)&&(lastday!=s[i].t)) sum++;
52         lastday = s[i].t;
53     }
54     printf("%d\n",sum);
55     return 0;
56 }