uva11987 Almost Union-Find(带删除并查集 动态开点思想)(bonus:abc279f)

题目

多组数据,n个数(n<=1e5),初始是n个单元素集,{1},{2},...,{n},

m(m<=1e5)次操作,操作分三种

1 p q  合并p和q所在集合,如果已在同一集合中,则忽略

2 p q  把p移动到q所在集合,如果已在同一集合中,则忽略

3 p 询问p所在元素个数及所有元素之和

对于每条类型3的指令,输出p所在元素个数及元素之和

思路来源

UVA11987(带权并查集的删除操作)_编码之夜的博客-优快云博客

题解

1和3都是常规的并查集操作,考虑2怎么维护

如果真的删除这个元素的话,需要考虑子节点的影响,

如果真的删掉的话,由于按秩合并可能还会到这个点,从而影响答案

所以并不真的删掉这个点x,而是将x的根root处减去x对于大小以及和的贡献,

记录id[x]映射的点号,新开一个点x用于放其大小及和,再把x挂到y所在的树根上去

诶感觉不判2操作在同一集合中的时候,这种写法好像没毛病啊,

然而就会WA,不知怎么回事……

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e5+10;
int n,m,op,u,v,par[N],sz[N],id[N],c;
ll sum[N];
void init(int n){
    for(int i=1;i<=n;++i){
        par[i]=sum[i]=id[i]=i;
        sz[i]=1;
    }
    c=n;
}
int find(int x){
    return par[x]==x?x:par[x]=find(par[x]);
}
void chg(int x){
    int fa=find(id[x]);
    sum[fa]-=x;
    sz[fa]--;
    id[x]=++c;
    par[c]=c;
    sum[c]=x;
    sz[c]=1;
}
void unite(int x,int y){
    x=find(x),y=find(y);
    if(x==y)return;
    par[y]=x;
    sz[x]+=sz[y];
    sum[x]+=sum[y];
}
int main(){
    while(~scanf("%d%d",&n,&m)){
        init(n);
        for(int i=1;i<=m;++i){
            scanf("%d",&op);
            if(op==1){
                scanf("%d%d",&u,&v);
                unite(id[u],id[v]);
            }
            else if(op==2){
                scanf("%d%d",&u,&v);
                if(find(id[u])==find(id[v])){//不加这个会WA是怎么回事
                    continue;
                }
                chg(u);
                unite(id[u],id[v]);
            }
            else{
                scanf("%d",&u);
                v=find(id[u]);
                printf("%d %lld\n",sz[v],sum[v]);
            }
        }
    }
    return 0;
}

Bonus(AtCoder Beginner Contest 279 F.BOX​​​​​​)​​​​​​

AtCoder Beginner Contest 279 F.BOX​​​​​​

初始n(n<=3e5)个盒子n个球,第i个盒子恰有i这个球

q(q<=3e5)次操作,操作分三种:

1. 把盒子y的所有球放入盒子x

2. 新建一个球,并放入盒子x

3. 输出球x现在在哪个盒子里

题解

也是删除之后,给盒子y再开一个点,

rt记录球映射到的点号 box记录盒子映射到的点号

par维护点的并查集关系 id记录点映射到的盒子号

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=6e5+10;
typedef pair<int,int> P;
int n,q,op,x,y,par[N],id[N],box[N],rt[N],c;
//boxi:原来的i盒子当前往boxi这个vector里放
//idi:vector映射的盒子号
//par:盒子的父亲关系
//rti:i球一开始放入的盒子号
//c:盒子对应的vector数 n:球数
int find(int x){
    return par[x]==x?x:par[x]=find(par[x]);
}
void merge(int x,int y){
    int u=find(box[x]),v=find(box[y]);
    if(u==v)return;
    par[v]=u;id[u]=x;
    box[y]=++c;id[c]=y;par[c]=c;
}
int main(){
    scanf("%d%d",&n,&q);
    for(int i=1;i<=n;++i){
        box[i]=++c,id[i]=i;
        par[i]=i,rt[i]=i;
    }
    for(int i=1;i<=q;++i){
        scanf("%d%d",&op,&x);
        if(op==1)scanf("%d",&y);
        if(op==1)merge(x,y);
        else if(op==2)rt[++n]=box[x];
        else printf("%d\n",id[find(rt[x])]);
    }
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小衣同学

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值