专题训练之并查集(带权并查集、可持久化并查集)

推荐一个博客:https://blog.csdn.net/tribleave/article/details/72878239 并查集和带权并查集

带权并查集:在并查集的基础上,对其中的每一个元素赋有某些值。在对并查集进行路径压缩和合并操作时,这些权值具有一定属性,即可将他们与父节点的关系,变化为与所在树的根结点关系。

 

带权并查集习题:

1.(POJ1182)http://poj.org/problem?id=1182

推荐几个讲解详细的博客:https://blog.csdn.net/niushuai666/article/details/6981689 从向量的角度看问题

分析:难点在于:A.路径压缩时的关系域的更新

B.条件判断时,若两个点的根节点不同则合并;若根节点相同则按照当前给定关系与原本的关系进行判断。

总的来说就是两点之间的偏移量大小的确定

注意:改组只有一组,多组读入会WA

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 using namespace std;
 5 const int maxn=5e4+10;
 6 struct node{
 7     int pre;
 8     int realtion;
 9 }f[maxn];
10 
11 int find(int x)
12 {
13     if ( !f[x].pre ) return x;
14     int tmp=f[x].pre;
15     f[x].pre=find(tmp);
16     f[x].realtion=(f[x].realtion+f[tmp].realtion)%3;
17     return f[x].pre;
18 }
19 
20 int main()
21 {
22     int n,k,op,a,b,rt1,rt2,ans;
23     scanf("%d%d",&n,&k);
24     ans=0;
25     memset(f,0,sizeof(f));
26     for ( int i=1;i<=k;i++ )
27     {
28         scanf("%d%d%d",&op,&a,&b);
29         if ( a>n || b>n )
30         {
31             ans++;
32             continue;
33         }
34         if ( op==2 && a==b )
35         {
36             ans++;
37             continue;
38         }
39         rt1=find(a);
40         rt2=find(b);
41         if ( rt1!=rt2 )
42         {
43             f[rt2].pre=rt1;
44             f[rt2].realtion=(f[a].realtion+op-1+3-f[b].realtion)%3;
45         }
46         else
47         {
48             if ( op==1 && f[a].realtion!=f[b].realtion )
49             {
50                 ans++;
51                 continue;
52             }
53             if ( op==2 && ((3-f[a].realtion+f[b].realtion)%3!=1) )
54             {
55                 ans++;
56                 continue;
57             }
58         }
59     }
60     printf("%d\n",ans);
61     return 0;
62 }
POJ1182

 

2.(POJ1988)http://poj.org/problem?id=1988

题意:初始时有N堆砖块,每堆有一个(编号为1-N)。现在有q个操作,

操作分2种:M X Y 将含编号X的那堆放置在编号为Y堆的上方

C X 含编号为X的砖块下方有多少个砖块

分析:设置数组d[i]表示编号为d的下方有多少块砖(同时也能表示编号为i的点到其所在树的树根的距离),d[i]初始化为0。还需要设置数组num[i]表示当i为一堆最下面的那个砖块(所在树的数根)总共包含多少个点。对于两种操作:查找时i到数根的距离等于i到原来数根的距离+原来数根到新树根的记录。合并时,原来的数根到新数根的距离为新树根包含点的个数,新树根点的个数加上原来数根点的个数。

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 using namespace std;
 5 const int maxn=3e4+10;
 6 int f[maxn],d[maxn],num[maxn];
 7 
 8 
 9 int find(int x)
10 {
11     if ( f[x]==-1 ) return x;
12     int tmp=find(f[x]);
13     if ( tmp==f[x] ) return f[x];
14     else
15     {
16         d[x]+=d[f[x]];
17         return f[x]=tmp;    
18     }
19 }
20 
21 int main()
22 {
23     int n,i,j,x,y,q;
24     char s[10];
25     while ( scanf("%d",&q)!=EOF )
26     {
27         for ( i=1;i<=30000;i++ )
28         {
29             f[i]=-1;
30             d[i]=0;
31             num[i]=1;
32         }
33         while ( q-- )
34         {
35             scanf("%s",s);
36             if ( s[0]=='M' )
37             {
38                 scanf("%d%d",&x,&y);
39                 int fx=find(x);
40                 int fy=find(y);
41                 if ( fx!=fy ) 
42                 {
43                     f[fx]=fy;
44                     d[fx]=num[fy];
45                     num[fy]+=num[fx];
46                 }
47             }
48             else
49             {
50                 scanf("%d",&x);
51                 int fx=find(x);
52                 printf("%d\n",d[x]);
53             }
54         }
55     }
56     return 0;
57 }
POJ1988

 

3.(HDOJ3047)http://acm.hdu.edu.cn/showproblem.php?pid=3047

题意:有n个人坐在zjnu体育馆里面,然后给出m个他们之间的距离, A B X, 代表B的座位比A多X. 然后求出这m个关系之间有多少个错误,所谓错误就是当前这个关系与之前的有冲突。

分析:大致用POJ1182,每个点的relation为其于其父节点的距离(也可以说是父节点到其的偏移量),为正代表父节点的座位号比该节点大,为负代表父节点的座位号比该节点小。每次更新时注意偏移量的变化(特别注意正负),每次访问若为同一个点则判断A到B的偏移量是否为x即可

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 using namespace std;
 5 const int maxn=5e4+10;
 6 struct node{
 7     int pre;
 8     int relation;
 9 }f[maxn];
10 
11 int find(int x)
12 {
13     if ( f[x].pre==-1 ) return x;
14     int tmp=f[x].pre;
15     f[x].pre=find(tmp);
16     f[x].relation=(f[x].relation+f[tmp].relation);
17     return f[x].pre;
18 }
19 
20 
21 int main()
22 {
23     int n,m,i,j,k,x,y,d,fx,fy,num,ans;
24     while ( scanf("%d%d",&n,&m)!=EOF )
25     {
26         for ( i=1;i<=n;i++ )
27         {
28             f[i].pre=-1;
29             f[i].relation=0;
30         }
31         ans=0;
32         while ( m-- )
33         {
34             scanf("%d%d%d",&x,&y,&d);
35             fx=find(x);
36             fy=find(y);
37             if ( fx==fy )
38             {
39                 if ( -f[x].relation+d+f[y].relation!=0 )
40                 {
41                     ans++;
42                     continue;
43                 }
44             }
45             else
46             {
47                 f[fx].pre=fy;
48                 f[fx].relation=-f[x].relation+d+f[y].relation;
49             }
50         }
51         printf("%d\n",ans);
52     }    
53     return 0;
54 }
HDOJ3047

 

posted @ 2018-08-02 09:28  HDU_jackyan  阅读(477)  评论(0编辑  收藏  举报