这是一道带权并查集的题目。
我们维护三个数组,f[x]表示节点x所在集合的代表元素(相当于合并之后的树根),size[x]表示以x为代表元素的集合的大小是多少(相当于一列战舰的数量),d[x]表示在这一列战舰中,x前面的战舰有多少。
因此对于每一次询问,答案就是|d[x]-d[y]|-1.
我们怎样维护这三个数组呢?其中第一个数组的维护很简单,就是并查集的维护,我们重点考虑后两个数组的维护。
在路径压缩时,在把x直接指向树根的同时,我们把d[x]更新为x到树根的距离,这样我们在并查集的find函数中就可以维护d,
而对于size的维护,我们在合并时直接将前一段战舰所在的size加上后一段的size即可。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <algorithm> 5 #include <cmath> 6 using namespace std; 7 int t; 8 int f[30010],size[30010],d[300010]; 9 int find(int x) { 10 if(f[x]==x) return x; 11 int xx=find(f[x]); 12 d[x]+=d[f[x]]; 13 return f[x]=xx; 14 } 15 int main() { 16 cin>>t; 17 for(int i=1;i<=30000;i++) { 18 size[i]=1; 19 f[i]=i; 20 d[i]=0; 21 } 22 while(t--) { 23 char c; 24 int x,y; 25 cin>>c>>x>>y; 26 int fx=find(x); 27 int fy=find(y); 28 if(c=='M') { 29 f[fx]=fy; 30 d[fx]=size[fy]; 31 size[fy]+=size[fx]; 32 } 33 else { 34 if(f[x]!=f[y]) puts("-1"); 35 else printf("%d ",abs(d[x]-d[y])-1); 36 } 37 } 38 return 0; 39 }