带权并查集:银河英雄传说
带权并查集,大体思路与普通并查集相仿,只不过要
多维护更多的东西:例如权值等。
以这道题为例,要维护两个战舰之间的数量,我们可
以维护一个战舰到其根节点的距离d,并查集首先要解
决“并“的问题,
inline void bing(int a,int b){
int xx=find(a),yy=find(b);
find(tail[yy]);
d[xx]=d[tail[yy]]+1;
fa[xx]=yy;
tail[yy]=tail[xx];
}
taill数组代表了某个”头“所对应的“尾”;
我们先不要管find的操作,其作用就是发现x列所对应
的“头“,并且更新某个点的d。很显然,当前点的d就等于
要并到的那一列的”尾“+1;其余的就如同普通并查集了。
我们再来看find的操作:
inline int find(int x){
if (fa[x]==x) return x;
else {
find(fa[x]);
d[x]=d[x]+d[fa[x]];
return fa[x]=fa[fa[x]];
}
}
这里有一个问题:为什么要先find,再更新d,最后
更新fa数组呢?因为fa还是存的之前的,我们先更新完它所对应的
d,然后显然的d[当前]要加上d[fa[当前点]],最后更新fa,不然就
没法顺水推舟般的更新当前点的d;
#include<iostream>
#include<cstdio>
#include<queue>
#include<algorithm>
const int maxn=100006;
using namespace std;
int t;
int fa[maxn],d[maxn],tail[maxn];
inline int abs_(int x,int y){
return x>y?x-y:y-x;
}
inline int find(int x){
if (fa[x]==x) return x;
else {
find(fa[x]);
d[x]=d[x]+d[fa[x]];
return fa[x]=fa[fa[x]];
}
}
inline void bing(int a,int b){
int xx=find(a),yy=find(b);
find(tail[yy]);
d[xx]=d[tail[yy]]+1;
fa[xx]=yy;
tail[yy]=tail[xx];
}
int main(){
for (int i=1;i<=30000;++i){
fa[i]=tail[i]=i;
d[i]=0;
}
cin>>t;
for (int i=1,a,b;i<=t;++i){
char q;
cin>>q;
scanf("%d%d",&a,&b);
// cout<<q<<endl;
if (q=='M'){
bing(a,b);
}
else {
if (fa[find(a)]!=fa[find(b)]) printf("-1\n");
else {
printf("%d\n",abs_(d[a],d[b])-1);
}
}
}
return 0;
}