Almost Union-Find
题目描述
输入输出格式
输入格式:
输出格式:
输入输出样例
输出样例#1:
3 12
3 7
2 8
分析:
不得不说是一道神奇的题目
如果只有操作1和操作3,那就是一道普通的带权并查集。然而,TM还有个毁天灭地的操作2。。。因为并查集是不支持删除操作的,所以我们得想个办法表示某一个元素在该集合中被删除。
这里我们用一个数组$id[i]$记录$i$节点当前的编号,如果我们删除了一个点,我们就把$id[i]$指向一个新的地址,在原本的集合中删除掉它的信息,然后把这个新的地址与要合并的集合合并。原本的点就基本可以当作是“被废弃了”,因为我们每次查找一个点的集合的时候就直接$find(id[x])$。当然,$id[x]$一开始就赋值为$x$。
不过貌似有巨佬用可持久化并查集$A$了,并不知道怎么用可持久化并查集做。。。。。。
$PS$:写了个启发式合并,读优输优,水了个最优解$hahahaha$。
Code:
//It is made by HolseLee on 22nd Aug 2018 //UVA11987 #include<cstdio> #include<cstring> #include<cstdlib> #include<cmath> #include<iostream> #include<iomanip> #include<algorithm> using namespace std; typedef long long ll; const int N=2e5+7; int n,m,id[N],fa[N],cnt[N],rk[N]; ll sum[N]; inline int read() { char ch=getchar();int num=0;bool flag=false; while(ch<'0'||ch>'9'){if(ch=='-')flag=true;ch=getchar();} while(ch>='0'&&ch<='9'){num=num*10+ch-'0';ch=getchar();} return flag?-num:num; } inline void print(int x) { if(x>9)print(x/10); putchar(x%10+'0'); } inline int find(int x) { return fa[x]==x?x:fa[x]=find(fa[x]); } inline void merge(int x,int y) { int fx=find(id[x]),fy=find(id[y]); if(rk[fx]>rk[fy]){ fa[fy]=fx; sum[fx]+=sum[fy]; cnt[fx]+=cnt[fy]; }else{ fa[fx]=fy; sum[fy]+=sum[fx]; cnt[fy]+=cnt[fx]; if(rk[fx]==rk[fy])rk[fy]++; } } inline void del(int x) { int fx=find(id[x]); cnt[fx]--;sum[fx]-=(ll)x; id[x]=++n; fa[id[x]]=id[x]; cnt[id[x]]=rk[id[x]]=1; sum[id[x]]=(ll)x; } int main() { while(scanf("%d%d",&n,&m)!=EOF){ for(int i=1;i<=n;++i)fa[i]=id[i]=i,sum[i]=(ll)i,cnt[i]=rk[i]=1; int op,x,y; for(int i=1;i<=m;++i){ op=read(); switch(op){ case 1: x=read();y=read(); if(find(id[x])!=find(id[y]))merge(x,y); break; case 2: x=read();y=read(); if(find(id[x])!=find(id[y])){ del(x);merge(x,y); } break; case 3: x=read(); print(cnt[find(id[x])]); putchar(' '); print(sum[find(id[x])]); putchar(' '); break; } } } return 0; }