uva11987 Almost Union-Find(並查集/帶刪除 開點思想)

題目

多組數據,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所在元素個數及元素之和

思路來源

https://blog.csdn.net/u013447865/article/details/39717737

題解

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;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章