并查集练习1

举头望明月,低头敲代码。。。

推荐学习地址:http://www.cnblogs.com/cyjb/p/UnionFindSets.html

简单:

hdu1213 How Many Tables:新手必秒

 1 #include<cstdio>
 2 const int N=1001;
 3 int f[N];
 4 void init(int n){
 5     for(int i=1;i<=n;++i)
 6         f[i]=i;
 7 }
 8 int fin(int x){
 9     if(x!=f[x])f[x]=fin(f[x]);
10     return f[x];
11 }
12 void uni(int x,int y){
13     if((x=fin(x))==(y=fin(y)))return;
14     else f[x]=y;
15 }
16 int main(){
17     int t,i,n,m,a,b,ans;
18     scanf("%d",&t);
19     while(t--){
20         scanf("%d%d",&n,&m);
21         init(n);
22         while(m--){
23             scanf("%d%d",&a,&b);
24             uni(a,b);
25         }
26         ans=0;
27         for(i=1;i<=n;++i)
28             if(f[i]==i)
29                 ans++;
30         printf("%d\n",ans);
31     }
32 }

hdu1232 畅通工程新手必秒

 1 #include<cstdio>
 2 const int N=1001;
 3 int f[N];
 4 void init(int n){
 5     for(int i=1;i<=n;++i)
 6         f[i]=i;
 7 }
 8 int fin(int x){
 9     if(x!=f[x])f[x]=fin(f[x]);
10     return f[x];
11 }
12 void uni(int x,int y){
13     if((x=fin(x))==(y=fin(y)))return;
14     else f[x]=y;
15 }
16 int main(){
17     int i,n,m,a,b,ans;
18     while(scanf("%d%d",&n,&m),n){
19         init(n);
20         while(m--){
21             scanf("%d%d",&a,&b);
22             uni(a,b);
23         }
24         ans=0;
25         for(i=1;i<=n;++i)
26             if(f[i]==i)
27                 ans++;
28         printf("%d\n",ans-1);
29     }
30 }

hdu1272 小希的迷宫:判断连通无环图。判连通只需判断根节点数为1,判环只需判断输入边的两个点是否有公共父节点。

 1 #include<cstdio>
 2 const int N=100001;
 3 int f[N];
 4 int v[N];//标记结点是否在图中
 5 int t;//判断是否有回路
 6 void init(){
 7     for(int i=1;i<N;++i){
 8         f[i]=i; v[i]=0;
 9     }
10 }
11 int fin(int x){
12     if(x!=f[x])f[x]=fin(f[x]);
13     return f[x];
14 }
15 void uni(int x,int y){
16     if((x=fin(x))==(y=fin(y))) t=1;
17     else f[x]=y;
18 }
19 int main(){
20     int i,a,b,cnt;
21     while(scanf("%d%d",&a,&b)==2){
22         if(a==-1&&b==-1)break;
23         init();
24         t=0;
25         cnt=0;//树的棵数,大于1则为森林
26         if(a==0&&b==0){//空树(无结点)
27             printf("Yes\n");continue;
28         }
29         while(a){
30             if(t==0) {uni(a,b);v[a]=v[b]=1;}
31             scanf("%d%d",&a,&b);
32         }
33         for(i=1;i<N;++i)
34             if(v[i]&&fin(i)==i) cnt++;
35         if(cnt==1&&t==0)printf("Yes\n");
36         else printf("No\n");
37     }
38 }

hdu1325 Is It A Tree?:判断是否为一棵树。与上题相比只需再判断除了根节点,是否每个结点的入度为1,再修改输出格式即可。

 1 #include<cstdio>
 2 const int N=100001;
 3 int f[N];
 4 int v[N];//标记结点是否在图中
 5 int in[N];//入度
 6 int t;
 7 void init(){
 8     for(int i=1;i<N;++i){
 9         f[i]=i; v[i]=0; in[i]=0;
10     }
11 }
12 int fin(int x){
13     if(x!=f[x])f[x]=fin(f[x]);
14     return f[x];
15 }
16 void uni(int x,int y){
17     if((x=fin(x))==(y=fin(y))) t=1;
18     else f[x]=y;
19 }
20 int main(){
21     int i,a,b,cnt,k=1;
22     while(scanf("%d%d",&a,&b)==2){
23         if(a<0&&b<0)break;
24         init();
25         t=0;
26         cnt=0;//树的棵数,大于1则为森林
27         if(a==0&&b==0){//空树(无结点)
28             printf("Case %d is a tree.\n",k++);continue;
29         }
30         while(a){
31             if(t==0) {uni(a,b);v[a]=v[b]=1;in[b]++;}
32             if(in[b]>1)t=1;
33             scanf("%d%d",&a,&b);
34         }
35         for(i=1;i<N;++i)
36             if(v[i]&&fin(i)==i) cnt++;
37         if(cnt==1&&t==0)printf("Case %d is a tree.\n",k++);
38         else printf("Case %d is not a tree.\n",k++);
39     }
40 }

hdu1856 More is better:求最大集合中元素个数。注意朋友对为0时答案为1.

 1 #include<cstdio>
 2 const int N=1e7+1;
 3 int f[N];
 4 int ma;
 5 void init(){
 6     for(int i=1;i<N;++i)
 7         f[i]=-1;
 8 }
 9 int fin(int x){
10     if(f[x]<0) return x;
11     f[x]=fin(f[x]);
12     return f[x];
13 }
14 void uni(int x,int y){
15     if((x=fin(x))==(y=fin(y))) return;
16     else{
17         f[x]+=f[y];
18         f[y]=x;
19         if(-f[x]>ma) ma=-f[x];
20     }
21 }
22 int main(){
23     int n,a,b;
24     while(scanf("%d",&n)==1){
25         ma=1;
26         init();
27         while(n--){
28             scanf("%d%d",&a,&b);
29             uni(a,b);
30         }
31         printf("%d\n",ma);
32     }
33 }

有点难度:

hdu1116 Play on Words(欧拉回路,并查集判连通)

题意:判断是否可以经过重组使得每个单词的第一个字母与前一个单词最后一个字母相同来打开门。

题解:每个单词首尾两个字母是关键,看成顶点,每个单词看成连接首尾两个字母的一条有向边。判断有向图基图是否连通(使用并查集判断),再判断是否存在有向欧拉通路/回路。

 1 #include<cstdio>
 2 #include<cstring>
 3 using namespace std;
 4 const int N=26;
 5 int f[N],id[N],od[N],v[N];
 6 void init(){
 7     for(int i=0;i<N;++i){
 8         f[i]=i;
 9         id[i]=od[i]=v[i]=0;
10     }
11 }
12 int fin(int x){
13     if(x!=f[x])f[x]=fin(f[x]);
14     return f[x];
15 }
16 void uni(int x,int y){
17     if((x=fin(x))==(y=fin(y)))return;
18     else  f[x]=y;
19 }
20 int main(){
21     int t,i,a,b,n,cnt,q[N],c;
22     char s[1001];
23     scanf("%d",&t);
24     while(t--){
25         init();
26         scanf("%d",&n);
27         while(n--){
28             scanf("%s",s);
29             a=s[0]-‘a‘;
30             b=s[strlen(s)-1]-‘a‘;
31             od[a]++; id[b]++;
32             uni(a,b);
33             v[a]=v[b]=1;
34         }
35         for(cnt=i=0;i<N;++i)
36             if(v[i]&&fin(i)==i)
37                 cnt++;
38         if(cnt>1){puts("The door cannot be opened.");continue;}
39         for(c=i=0;i<N;++i)
40             if(v[i]&&od[i]!=id[i])
41             q[c++]=i;
42         if(c==0) puts("Ordering is possible.");
43         else if(c==2&&(od[q[0]]-id[q[0]]==1&&id[q[1]]-od[q[1]]==1||od[q[1]]-id[q[1]]==1&&id[q[0]]-od[q[0]]==1))
44             puts("Ordering is possible.");
45         else puts("The door cannot be opened.");
46     }
47     return 0;
48 }

hdu1198 Farm Irrigation:求连通区域个数。

 1 #include<cstdio>
 2 #include<cstring>
 3 using namespace std;
 4 const int N=51;
 5 int f[N*N];
 6 char g[N][N];
 7 //上右下左,有出口为1
 8 int pipe[11][4]={{1,0,0,1},{1,1,0,0},{0,0,1,1},{0,1,1,0},{1,0,1,0},{0,1,0,1},{1,1,0,1},{1,0,1,1},{0,1,1,1},{1,1,1,0},{1,1,1,1}};
 9 int n,m;
10 void init(){
11     for(int i=0;i<n*m;++i)
12         f[i]=i;
13 }
14 int fin(int x){
15     if(x!=f[x])f[x]=fin(f[x]);
16     return f[x];
17 }
18 void uni(int x,int y){
19     if((x=fin(x))==(y=fin(y)))return;
20     else  f[x]=y;
21 }
22 int main(){
23     int i,j,cnt;
24     while(scanf("%d%d",&m,&n)==2){
25         if(m<0||n<0)break;
26         init();
27         for(i=0;i<m;++i) scanf("%s",g[i]);
28         for(i=0;i<m;++i){
29             for(j=0;j<n;++j){
30                 if(i>0&&pipe[g[i][j]-‘A‘][0]==1&&pipe[g[i-1][j]-‘A‘][2]==1)
31                     uni(i*n+j,(i-1)*n+j);
32                 if(j>0&&pipe[g[i][j]-‘A‘][3]==1&&pipe[g[i][j-1]-‘A‘][1]==1)
33                     uni(i*n+j,i*n+j-1);
34             }
35         }
36         for(cnt=i=0;i<m*n;++i)
37             if(fin(i)==i)cnt++;
38         printf("%d\n",cnt);
39     }
40     return 0;
41 }

hdu3635 Dragon Balls:记录移动次数。

 1 #include<cstdio>
 2 #include<cstring>
 3 using namespace std;
 4 const int N=10001;
 5 int f[N],num[N];
 6 int c[N];//移动次数
 7 int n;
 8 void init(){
 9     for(int i=1;i<=n;++i){
10         f[i]=i; c[i]=0; num[i]=1;
11     }
12 }
13 int fin(int x){
14     if(x==f[x])return x;
15     int t=f[x];
16     f[x]=fin(f[x]);
17     c[x]+=c[t];//本身加上父节点的移动次数
18     return f[x];
19 }
20 void uni(int x,int y){
21     if((x=fin(x))==(y=fin(y)))return;
22     else{
23         f[x]=y;
24         num[y]+=num[x];
25         c[x]=1;//根节点移动一次
26     }
27 }
28 int main(){
29     int t,q,a,b,cnt,k=1;
30     char s[3];
31     scanf("%d",&t);
32     while(t--){
33         scanf("%d%d",&n,&q);
34         init();
35         printf("Case %d:\n",k++);
36         while(q--){
37             scanf("%s",s);
38             if(s[0]==‘T‘){
39                 scanf("%d%d",&a,&b);
40                 uni(a,b);
41             }
42             else{
43                 scanf("%d",&a);
44                 int x=fin(a);
45                 printf("%d %d %d\n",x,num[x],c[a]);
46             }
47         }
48     }
49     return 0;
50 }

hdu2473 Junk-Mail Filter:并查集删点,对每个结点建立虚根。

 1 #include<cstdio>
 2 #include<cstring>
 3 using namespace std;
 4 const int N=1200001;
 5 int f[N],v[N];
 6 int n,nn,m;
 7 void init(){
 8     for(int i=0;i<n;++i) f[i]=i+n;
 9     for(int i=n;i<nn+m;++i) f[i]=i;
10 }
11 int fin(int x){
12     if(x!=f[x])f[x]=fin(f[x]);
13     return f[x];
14 }
15 void uni(int x,int y){
16     if((x=fin(x))==(y=fin(y)))return;
17     else  f[x]=y;
18 }
19 int main(){
20     int t,i,a,b,cnt,k=1;
21     char s[3];
22     while(scanf("%d%d",&n,&m),n||m){
23         nn=2*n;
24         memset(v,0,sizeof(v));
25         init();
26         while(m--){
27             scanf("%s",s);
28             if(s[0]==‘M‘){
29                 scanf("%d%d",&a,&b);
30                 uni(a,b);
31             }
32             else{
33                 scanf("%d",&a);
34                 f[a]=nn++;
35             }
36         }
37         for(cnt=i=0;i<n;++i)
38             if(v[fin(i)]==0)
39                 {v[fin(i)]=1;cnt++;}
40         printf("Case #%d: %d\n",k++,cnt);
41     }
42     return 0;
43 }

hdu3172 Virtual Friends:映射。(吐槽:输入坑爹- -+)

 1 #include<cstdio>
 2 #include<map>
 3 #include<iostream>
 4 using namespace std;
 5 const int N=100001;
 6 int f[N],num[N];
 7 int n;
 8 void init(){
 9     for(int i=1;i<N;++i){
10         f[i]=i; num[i]=1;
11     }
12 }
13 int fin(int x){
14     if(x!=f[x])f[x]=fin(f[x]);
15     return f[x];
16 }
17 void uni(int x,int y){
18     if((x=fin(x))==(y=fin(y)))
19         printf("%d\n",num[x]);
20     else{
21         f[x]=y;
22         num[y]+=num[x];
23         printf("%d\n",num[y]);
24     }
25 }
26 map<string,int>p;
27 int main(){
28     int t,cnt;
29     char a[21],b[21];
30     while(scanf("%d",&t)==1){
31         while(t--){
32             cnt=1;
33             scanf("%d",&n);
34             init();
35             p.clear();
36             while(n--){
37                 scanf("%s %s",a,b);
38                 if(!p[a])p[a]=cnt++;
39                 if(!p[b])p[b]=cnt++;
40                 uni(p[a],p[b]);
41             }
42         }
43     }
44     return 0;
45 }

hdu1558 Segment set:并查集,线段相交(快速跨立排斥试验看不懂,留着等以后我开窍了再学。。。)

 1 #include<cstdio>
 2 #include<algorithm>
 3 using namespace std;
 4 const int N=1001;
 5 int f[N],num[N];
 6 int n;
 7 struct Point{
 8     double x,y;
 9 }s[N],e[N];
10 double mul(Point a,Point c,Point b){
11     return (c.x-a.x)*(b.y-a.y)-(b.x-a.x)*(c.y-a.y);
12 }
13 bool LineSegmentIntersect(Point a,Point b,Point c,Point d){
14     if(min(a.x,b.x)>max(c.x,d.x)) return 0;
15     if(min(a.y,b.y)>max(c.y,d.y)) return 0;
16     if(max(a.x,b.x)<min(c.x,d.x)) return 0;
17     if(max(a.y,b.y)<min(c.y,d.y)) return 0;
18     if(mul(a,c,b)*mul(a,b,d)<0) return 0;
19     if(mul(c,a,d)*mul(c,d,b)<0) return 0;
20     return 1;
21 }
22 void init(){
23     for(int i=1;i<=n;++i){
24         f[i]=i; num[i]=1;
25     }
26 }
27 int fin(int x){
28     if(x!=f[x])f[x]=fin(f[x]);
29     return f[x];
30 }
31 void uni(int x,int y){
32     if((x=fin(x))==(y=fin(y)))return;
33     else{
34         f[x]=y;
35         num[y]+=num[x];
36     }
37 }
38 int main(){
39     int t,cnt,i,j,k;
40     char a[3];
41     scanf("%d",&t);
42     while(t--){
43         scanf("%d",&n);
44         init();
45         i=0;
46         while(n--){
47             scanf("%s",a);
48             if(a[0]==‘P‘){
49                 i++;
50                 scanf("%lf%lf%lf%lf",&s[i].x,&s[i].y,&e[i].x,&e[i].y);
51                 for(j=1;j<i;++j){
52                     if(LineSegmentIntersect(s[i],e[i],s[j],e[j]))
53                         uni(i,j);
54                 }
55             }
56             else{
57                 scanf("%d",&k);
58                 printf("%d\n",num[fin(k)]);
59             }
60         }
61         if(t)printf("\n");
62     }
63     return 0;
64 }

hdu3461 Code Lock(并查集,二分求幂)英语渣表示第一遍看完题目一脸懵逼,然后直接百度题意orz

 1 #include<cstdio>
 2 #include<algorithm>
 3 using namespace std;
 4 const int N=1e7+5;
 5 const int mod=1e9+7;
 6 typedef long long ll;
 7 int f[N];
 8 int n;
 9 int pow(int n){
10     ll a=26;
11     ll r=1;
12     while(n){
13         if(n&1) r=(r*a)%mod;
14         a=(a*a)%mod;
15         n>>=1;
16     }
17     return int(r);
18 }
19 void init(){
20     for(int i=1;i<=n+1;++i){
21         f[i]=i;
22     }
23 }
24 int fin(int x){
25     if(x!=f[x])f[x]=fin(f[x]);
26     return f[x];
27 }
28 int uni(int x,int y){
29     if((x=fin(x))==(y=fin(y)))return 0;
30     else{
31         f[x]=y;
32         return 1;
33     }
34 }
35 int main(){
36     int m,cnt,a,b;
37     while(scanf("%d%d",&n,&m)==2){
38         init();
39         cnt=0;//"不同"的区间数
40         while(m--){
41             scanf("%d%d",&a,&b);
42             cnt+=uni(a,b+1);//[1,3]、[4,5]和[1,5]“相同”,uni(l,r+1)即可判断
43         }
44         printf("%d\n",pow(n-cnt));
45     }
46     return 0;
47 }

时间: 2024-08-02 11:03:59

并查集练习1的相关文章

CodeForces 745C Hongcow Builds A Nation 并查集

题意: 给了你n个城市 m条边 k个政府 每个政府管辖的区域内不能和其他政府的区域有相连 即政府之间不存在路径 问你在维护这种关系的同时 最多再加多少条边 思路: 先找出来每个联通块 再找出来没有归属的孤立的点 把他们都放到最大的联通块里 然后每个联通块之间的点两两连边是n*(n-1)/2条边 最后算出来的ans-m就好了 (看别人的博客学了一个max_element 1 #include<bits/stdc++.h> 2 #define cl(a,b) memset(a,b,sizeof(a

并查集(个人模版)

并查集: 1 int find(int a) 2 { 3 int r=a; 4 while(f[r]!=r) 5 r=f[r]; 6 int i=a; 7 int j; 8 while(i!=r) 9 { 10 j=f[i]; 11 f[i]=r; 12 i=j; 13 } 14 return r; 15 } 16 int merge(int a,int b) 17 { 18 int A,B; 19 A=find(a); 20 B=find(b); 21 if(A!=B) 22 { 23 f[B

并查集应用

题目描述: One way that the police finds the head of a gang is to check people's phone calls. If there is a phone call between A and B, we say that A and B is related. The weight of a relation is defined to be the total time length of all the phone calls

【bzoj3674】 可持久化并查集加强版

http://www.lydsy.com/JudgeOnline/problem.php?id=3674 (题目链接) 题意 维护并查集3个操作:合并:回到完成第k个操作后的状态:查询. Solution 其实就是用主席树的叶子节点维护并查集的可持久化数组fa[]. 细节 终于认识到了按秩合并的强大,单纯写个路径压缩Re飞,写了路径压缩+按秩合并比单纯的按秩合并每快多少→_→ 代码 // bzoj3674 #include<algorithm> #include<iostream>

BZOJ1015[JSOI2008]星球大战starwar[并查集]

1015: [JSOI2008]星球大战starwar Time Limit: 3 Sec  Memory Limit: 162 MBSubmit: 5253  Solved: 2395[Submit][Status][Discuss] Description 很久以前,在一个遥远的星系,一个黑暗的帝国靠着它的超级武器统治者整个星系.某一天,凭着一个偶然的机遇,一支反抗军摧毁了帝国的超级武器,并攻下了星系中几乎所有的星球.这些星球通过特殊的以太隧道互相直接或间接地连接. 但好景不长,很快帝国又重

HDU 5606 tree 并查集

tree 把每条边权是1的边断开,发现每个点离他最近的点个数就是他所在的连通块大小. 开一个并查集,每次读到边权是0的边就合并.最后Ans?i??=size[findset(i)],size表示每个并查集根的size Ans_i=size[findset(i)],sizeAns?i??=size[findset(i)],size表示每个并查集根的sizesize. #include<cstdio> #include<cstring> #include<algorithm>

HDU 5441 离线处理 + 并查集

题意:给n个节点m条带权值边的无向图.然后q个问题,每次询问点对的数目,点对需要满足的条件是:1)连通:2)其路径的最大权值不能超过询问值. 分析:如果没次询问一次,dfs一次,很可能超时,因此可以用并查集.离线处理,把边按权值排序,把问题按大小排序.然后离线的过程就是不断向图中加边的过程. 比如样例如下: 然后离线处理,排完序后将会是一条一条的加边:问题也排了序,因此是个累加过程... 1 #include <cstdio> 2 #include <iostream> 3 #in

poj1988 Cube Stacking(并查集

题目地址:http://poj.org/problem?id=1988 题意:共n个数,p个操作.输入p.有两个操作M和C.M x y表示把x所在的栈放到y所在的栈上(比如M 2 6:[2 4]放到[1 6]上为[2 4 1 6]),C x为输出x下面有几个数. 思路:并查集每个集合以栈最下面的数为根,维护两个数组num[x]表示x所在集合节点总数,count[x]表示x下方节点个数.每次查找压缩路径的时候更新count(换父节点的时候每轮都把父节点的count加给儿子,就可以一直更新到x所在栈

习题:过路费(kruskal+并查集+LCA)

过路费  [问题描述]在某个遥远的国家里,有 n 个城市.编号为 1,2,3,…,n.这个国家的政府修 建了 m 条双向道路,每条道路连接着两个城市.政府规定从城市 S 到城市 T 需 要收取的过路费为所经过城市之间道路长度的最大值.如:A 到 B 长度为 2,B 到 C 长度为 3,那么开车从 A 经过 B 到 C 需要上交的过路费为 3. 佳佳是个做生意的人,需要经常开车从任意一个城市到另外一个城市,因此 他需要频繁地上交过路费,由于忙于做生意,所以他无时间来寻找交过路费最低 的行驶路线.然

HDU3081Marriage Match II(二分答案+并查集+最大流SAP)经典

Marriage Match II Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 2507    Accepted Submission(s): 856 Problem Description Presumably, you all have known the question of stable marriage match. A