【牛客】并查集-Professional Manager(题解+学习笔记)
https://ac.nowcoder.com/acm/problem/15696
今天莫名奇妙被这题卡了好久,后来想想确实题解还是有我需要学习的地方。
一开始用的暴力枚举TLE了,不过也在意料之中。
但是第一遍的代码是超时在记录每株树的元素个数,第一次是用遍历的方法来更新,时间复杂度是O(n^2),一定超时。
而且观察题目,感觉只能从这里下手。
于是可以只储存每株树根的元素个数。但是这个的难点在于删除结点时,如果删除的是根结点,会比较难操作。
我们用一个新数组来记录结点的【下标】,它默认为结点一开始的序号,但是中途会有从树中移除的,就给他一个新的序号,从n+1开始给,逐渐添加,
所以我们合并的不是结点本身,而是他的下标。具体看代码。
#include<bits/stdc++.h>
using namespace std;
const int maxx=200010;
int par[maxx];
int num[maxx];//在根上记录结点个数
int id[maxx];//储存下标
int find(int x){//找根
if(x==par[x])return x;
return par[x]=find(par[x]);
}
int main(){
int t; cin>>t;
int cnt=1;
while(t--){
printf("Case #%d:\n",cnt++);
int n,q; cin>>n>>q;
for(int i=1;i<maxx;i++){//初始化
par[i]=id[i]=i;
num[i]=1;
}
int x,u,v;
for(int i=0;i<q;i++){
cin>>x;
if(x==1){
cin>>u>>v;
int root_u=find(id[u]);//查找的是id[]而不是下标了
int root_v=find(id[v]);
if(root_u!=root_v){//并查集
par[root_u]=root_v;
num[root_v]+=num[root_u];//顺序不能反,父亲结点继承子结点的num值
}
}else if(x==2){
cin>>u;
int root_u=find(id[u]);
num[root_u]--;//该根要少一个结点了
id[u]=++n;//给一个新的下标
}else if(x==3){
cin>>u;
int root_u=find(id[u]);
cout<<num[root_u]<<endl;
}else{
cin>>u>>v;
if(find(id[u])==find(id[v]))cout<<"YES\n";
else cout<<"NO\n";
}
}
}
return 0;
}