P1196 [NOI2002]银河英雄传说(带权并查集)

时间:2021-06-28 10:01:27

这个题的题目背景很是宏大,什么宇宙战舰的都出来了。但细细一看,我们就会发现,这是带权并查集的题目,首先我们还是像之前在并查集中的操作一样,但在这里我们还是应该开数组来维护所要加的权值,两个战舰是否在同一个队列中好判断,关键是他们间的间隔,实际上就是他们的权值之和的绝对值再减一,代码如下

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e6+;
int fa[maxn];
int va[maxn];//该点的权值
int num[maxn];//到父亲节点的距离当前队列的长度
int n,x,y;
char opt[maxn];
int fid(int x)
{
if(x==fa[x])
{
return x;
}
int t=fa[x];
fa[x]=fid(fa[x]);//路径压缩
va[x]+=va[t]; //回溯时注意要更新
num[x]=num[t]; //x所在队列的长度就是父亲节点所在队列的长度
return fa[x];
}
void united(int x,int y)
{
int f1=fid(x);
int f2=fid(y);
if(f1==f2)
{
return;
}
fa[f1]=f2;//将x所在队列的对头加入y的队列
va[f1]+=num[f2];//x所在队列的父节点的最新权值
num[f1]+=num[f2];//更新当前队列的长度
num[f2]=num[f1];
}
int check(int x,int y)//判断是否在同一集合中
{
int f1=fid(x);
int f2=fid(y);
if(f1==f2)
{
return ;
}
else
{
return ;
}
}
int main()
{
scanf("%d",&n);
for(int i=;i<=n;i++)
{
fa[i]=i;
va[i]=;
num[i]=;
}
for(int i=;i<=n;i++)
{
scanf("%s",opt);
if(opt[]=='M')
{
scanf("%d%d",&x,&y);
united(x,y);
}
else
{
scanf("%d%d",&x,&y);
if(check(x,y))
{
printf("%d\n",abs(va[x]-va[y])-);
}
else
{
printf("-1\n");
}
}
}
return ;
}