题意

操作1:将两点所在的区间合并。

操作2:将一点移至另一点所在集合内。

操作3:求一点所在区间的元素个数和元素的总和。

分析

并查集的大小以及元素和是很好计算的,在合并时将“认亲”的点的对应值加给它祖先就行了。主要是操作2。

对于一个点单独抽离出来,我们发现是不好弄的,按照常规的方法做的话,如果直接以它向集合的祖先相连,则它的子孙们也会受影响。而去除这一影响的关键就在于建虚点。即用一个实际并不存在的点来代表集合的祖先,这样挪走集合内的任意一个点,对其它点都不会造成影响,因为没有人会管它。于是我们在初始给每个点创造一个属于它自己的虚点,照常合并即可。

代码

#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,fa[200005],m,x,y,op;
int sum[200005],siz[200005];
int p,q;
inline void read(int &res){
	char c;
	int f=1;
	res=0;
	c=getchar();
	while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
	while(c>='0'&&c<='9')res=(res<<1)+(res<<3)+c-48,c=getchar();
	res*=f;
}
inline int find(int x){
	if(fa[x]!=x)return fa[x]=find(fa[x]);//压缩路径 
	return x;
}
signed main(){
	while(cin>>n>>m){
		for(int i=1;i<=n;i++)fa[i]=i+n,sum[i+n]=i;
		for(int i=n+1;i<=2*n;i++)fa[i]=i,siz[i]=1;//建虚点 
		while(m--){
			cin>>op;
			if(op==1){
				read(p);read(q);
				int x=find(p),y=find(q);
				if(x==y)continue;
				if(siz[x]<siz[y]){//按秩合并 
					fa[x]=y;
					sum[y]+=sum[x];//合并 
					siz[y]+=siz[x];
				}
				else {
					fa[y]=x;
					sum[x]+=sum[y];
					siz[x]+=siz[y];
				}
			}
			if(op==2){
				read(p);read(q);
				int x=find(p),y=find(q);
				if(x==y)continue;
				fa[p]=y;
				sum[y]+=p;
				sum[x]-=p;
				siz[y]++;
				siz[x]--;
			}
			if(op==3){
				read(q);
				int x=find(q);
				cout<<siz[x]<<" "<<sum[x]<<endl;
			}
		}
	}
	return 0;
} 
posted on 2021-08-28 21:06  漠寒·  阅读(28)  评论(0编辑  收藏  举报