P1196 [NOI2002]银河英雄传说(带权并查集)
这个题的题目背景很是宏大,什么宇宙战舰的都出来了。但细细一看,我们就会发现,这是带权并查集的题目,首先我们还是像之前在并查集中的操作一样,但在这里我们还是应该开数组来维护所要加的权值,两个战舰是否在同一个队列中好判断,关键是他们间的间隔,实际上就是他们的权值之和的绝对值再减一,代码如下
#include<bits/stdc++.h> using namespace std; const int maxn=1e6+7; 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 1; } else { return 0; } } int main() { scanf("%d",&n); for(int i=1;i<=n;i++) { fa[i]=i; va[i]=0; num[i]=1; } for(int i=1;i<=n;i++) { scanf("%s",opt); if(opt[0]=='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])-1); } else { printf("-1\n"); } } } return 0; }