• 并查集的删除操作


    题目大意:有n个集合,提供三种操作
    1 p q:将p所在的集合和q所在的集合并起来
    2 p q:将p元素移到集合q所在的集合
    3 p:求出p所在集合有多少个元素并输出这些元素的和

    Sample Input
    5 7
    1 1 2  //集合1与2合并(集合操作)
    2 3 4  //元素3放到元素4所在的集合中
    1 3 5  //将[3,4]与[5]合并,得到[3,4,5]
    3 4   //查询4所在的集合
    2 4 1 //将元素4移动到1所在集合,得到[1,2,4]
    3 4
    3 3  //查询3所在的集合[3,5]

    Sample Output //每查询一次,输出一次
    3 12  //4所在的集合有3个元素,和为12
    3 7  
    2 8

    sol:开始时,n个点,n个集合,然后依次实现题目中的3种操作。在实现移动操作时,由于我们不知道该点在原集合中是整个集合的根还是叶子结点或其它中间结点,如果是叶子结点,操作简单,但如果是非叶子结点,特别是根结点,该集合中的其它结点的操作都与之息息相关,就不好操作了。所以我们采取的方法是将原点留在那里,只是消除它在原集合中根结点那的影响力,同时我们将该点的集合编号变更成其它的新编号,即开个虚点出来,该点单独成集合,然后与目标集合进行合并操作就好了。

    代码实现:

     1 #include <iostream>
     2 #include <cstdio>
     3 using namespace std;
     4 #define maxn 200018
     5 int father[maxn],idx[maxn],num[maxn];
     6 long long int sum[maxn];
     7 int n,m,cnt;
     8 int find(int x)
     9 {
    10     if(x==father[x])return x;
    11     return find(father[x]);
    12 }
    13 void init()
    14 {
    15     for(int i=1;i<=n;i++)
    16     {
    17         father[i]=idx[i]=sum[i]=i;
    18         //idx[i]表示i当前的集合编号,sum[i]表示以i为根的集合的元素和 
    19         num[i]=1;//num[i]表示以i为根的集合元素个数 
    20     }
    21     cnt=n;
    22 }
    23 void Union(int p,int q)
    24 {
    25     int pp=find(idx[p]),qq=find(idx[q]);
    26     father[pp]=qq;
    27     num[qq]+=num[pp];
    28     sum[qq]+=sum[pp];
    29 }
    30 void Delete(int p)
    31 {
    32     int pp=idx[p];
    33     //取出p所在集合的真实编号,因为存在删除操作,p所在的集合编号是不断变化的 
    34     sum[find(pp)]-=p; //消除p在原集合中的影响力 
    35     num[find(pp)]--; 
    36     idx[p]=++cnt; //新加一个集合出来,作为p的新集合编号 
    37     sum[idx[p]]=p; //以下是给p的新集合给初值 
    38     num[idx[p]]=1;
    39     father[idx[p]]=idx[p];
    40 }
    41 int main()
    42 {
    43     while(scanf("%d%d",&n,&m)==2)
    44     {
    45         init();
    46         int ope,p,q;
    47         for(int i=1;i<=m;i++)
    48         {
    49             scanf("%d",&ope);
    50             if(ope==1)
    51             {
    52                 scanf("%d%d",&p,&q);
    53                 if(find(idx[p])==find(idx[q]))
    54                 continue;
    55                 else 
    56                 Union(p,q);
    57             }
    58             else if(ope==2)
    59             {
    60                 scanf("%d%d",&p,&q);
    61                 if(find(idx[p])!=find(idx[q]))
    62                 {
    63                     Delete(p); 
    64                     Union(p,q);
    65                 }
    66             }
    67             else 
    68             {
    69                 int u;
    70                 scanf("%d",&u);
    71                 int fuck=find(idx[u]);
    72                 printf("%d %lld
    ",num[fuck],sum[fuck]);
    73             }
    74         }
    75     }
    76     return 0;
    77 }
  • 相关阅读:
    sql查询重复记录、删除重复记录方法大全
    查询字段所在的表/视图
    查询某张表被哪些存储过程或者视图用到的sql语句
    SQL中char、varchar、nvarchar的区别
    JS快速获取图片宽高的方法
    Git代码冲突常见解决方法
    HTML__图片轮播ion-slide-box
    oracle列出两个日期间所有日期
    myeclipse 8.0 注册码
    网页中图片旋转的几种实现方式
  • 原文地址:https://www.cnblogs.com/cutepota/p/12487571.html
Copyright © 2020-2023  润新知