P1196

作为带权并查集的模板题,当然要先烧烤,再Σ
看这:

更新很容易,我们来分析一下:对于原来的队头,它到队头的距离为0,当将它所在的队列移到另一个队列后面时,它到队头的距离就是排在它前面的飞船数,也就是合并前另一个队列的飞船数量。因此,就知道该怎样实现了,我们再建一个数组num,num[i]表示以i为队头的队列的飞船数量,初始时都是1,在每次合并的时候,fx为合并前飞船i的队头,fy为合并前飞船j的队头,每次合并时,先更新front[fx],即给它加上num[fy],然后开始合并,即fa[fx]=fy,最后更新num,num[fy]+=num[fx];num[fx]=0。

现在就差最后一步了:如何计算每个飞船到队头的距离。再来分析一下:对于任意一个飞船,我们都知道它的祖先(不一定是队头,但一定间接或直接指向队头),还知道距离它祖先的距离。对于每一个飞船,它到队头的距离,就等于它到它祖先的距离加上它祖先到队头的距离,而它的祖先到队头的距离,也可以变成类似的。可以递归实现,由于每一次更新都要用到已经更新完成的祖先到队头的距离,所以要先递归找到队头,然后在回溯的时候更新(front[i]+=front[fa[i]]),可以把这个过程和查找队头的函数放在一起。

#include<bits/stdc++.h>
using namespace std;
int fa[30010],front[30010],num[30010],x,y,j,n,T,ans;
char ins;
int find(int n) {
	if(fa[n]==n)return fa[n];
	int fn=find(fa[n]);
	front[n]+=front[fa[n]];
	return fa[n]=fn;
}
int main() {
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cin>>T;
	for(int i=1;i<=30000;i++) {
		fa[i]=i;
		front[i]=0;
		num[i]=1;
	}
	while(T--){
		cin>>ins>>x>>y;
		int fx=find(x);
		int fy=find(y);
		if(ins=='M') {
			front[fx]+=num[fy];
			fa[fx]=fy;
			num[fy]+=num[fx];
			num[fx]=0;
		}
		if(ins=='C') {
			if(fx!=fy)cout<<"-1"<<endl;
			else cout<<abs(front[x]-front[y])-1<<endl;
		}
	}
	return 0;
}
posted @ 2025-03-20 19:57  yzc_is_SadBee  阅读(28)  评论(0)    收藏  举报