题目比较模板,但是也扩展了许多以前不知道的知识点,记录一下比较有启发性的题。
目录
1.并查集之删除操作---创点转移:
1和3都是并查集的基础操作,这里就不说了(以前讲过),我们主要看2,我们把2的操作拆分成先去除p,再合并p,那么我们如何删除呢?
我们可以真的去删,但是我们需要修改子节点的连接,会增加时间复杂度并且比较麻烦。
于是我们采用用空间换时间的策略,我们可以再创建一个点,当我们删3时,我们可以保留原来的位置,但是我们需要为3提供真实的点,于是我们用类似映射的方法real[3]=新创建的点,这样子,我们就把3的“空壳”留在原地方,而把3的“灵魂”存在了其他地方,这样其他的点也就不用操作了。
下面是AC代码:
#include<bits/stdc++.h>
using namespace std;
int n,m,p,q,c,fa[200010],real1[100005],cc;
struct node{
int cnt;
long long sum;
}root[200010];
int find(int x){
if(fa[x]==x) return x;
return fa[x]=find(fa[x]);
}
void merge(int x,int y){
root[find(y)].cnt+=root[find(x)].cnt;
root[find(y)].sum+=root[find(x)].sum;
fa[find(x)]=find(y);
}
void del(int x){
root[find(real1[x])].cnt--;
root[find(real1[x])].sum-=x;
real1[x]=++cc;
root[cc].cnt=1;
root[cc].sum=x;
fa[cc]=cc;
}
signed main(){
while(cin>>n>>m){
for(int i=1;i<=n;i++){
fa[i]=i;
real1[i]=i;
root[i].cnt=1;
root[i].sum=i;
}
cc=n;
for(int i=1;i<=m;i++){
cin>>c;
if(c==1){
cin>>p>>q;
if(find(real1[p])==find(real1[q])) continue;
merge(real1[p],real1[q]);
}
else if(c==2){
cin>>p>>q;
if(find(real1[p])==find(real1[q])) continue;
del(p);
merge(real1[p],real1[q]);
}
else{
cin>>p;
node ss=root[find(real1[p])];
cout<<ss.cnt<<" "<<ss.sum<<endl;
}
}}
}
这里有几点需要注意:
1.多组输入注意对会新创建的值的改变,这里的cc每次都应从n开始。
2.注意合并时的判断条件必不可少,因为这里的并查集带了权值,重复时会导致值的重复相加。
2.并查集之删除操作---逆向思考:
以前有讲过,这里找到了个题目算是填坑了。我们先记录删的,把删的全删了,这样从反方向就是创建了。
下面是AC代码:
#include<bits/stdc++.h>
using namespace std;
int n,m,x,y,k,fa[400010],huei[400010],head[400010],cnt,ck[400010];
int hh[400010];
struct node{
int dian,next;
}edge[400010];
void merge(int x,int y){
edge[++cnt].dian=y;
edge[cnt].next=head[x];
head[x]=cnt;
}
int find(int x){
if(fa[x]==x) return x;
return fa[x]=find(fa[x]);
}
void merge1(int x,int y){
fa[find(x)]=find(y);
}
int main(){
cin>>n>>m;
memset(head,-1,sizeof(head));
for(int i=1;i<=m;i++){
scanf("%d%d",&x,&y);
merge(x,y);
merge(y,x);
}
cin>>k;
for(int i=1;i<=k;i++){
scanf("%d",&huei[i]);
ck[huei[i]]=1;
}
for(int i=0;i<=n-1;i++) fa[i]=i;
for(int i=0;i<=n-1;i++){
if(ck[i]==1) continue;
if(head[i]==-1) continue;
for(int j=head[i];j!=-1;j=edge[j].next){
if(ck[edge[j].dian]==1) continue;
if(find(edge[j].dian)==find(i)) continue;
merge1(edge[j].dian,i);
}
}
int cc=0;
for(int i=0;i<=n-1;i++){
if(fa[i]==i&&ck[i]==0) cc++;
}
hh[k+1]=cc;
for(int i=k;i>=1;i--){
int gg=huei[i];
ck[gg]=0;
cc++;
for(int j=head[gg];j!=-1;j=edge[j].next){
if(ck[edge[j].dian]==1) continue;
if(find(edge[j].dian)!=find(gg)) cc--;
merge1(edge[j].dian,gg);
}
hh[i]=cc;
}
for(int i=1;i<=k+1;i++){
printf("%d\n",hh[i]);
}
}