题目链接:银河英雄传说
边带权并查集,舰队在合并的过程中带着它下面的舰一起走,d[x]记录战舰与fa[x]战舰之间的权值,在路径压缩的时候,
把d[x]更新为从x到树根的路径上得到所有边权之和吗,d[x]既保存了位于x之前的战舰数量。
当接收到一个Cx y指令时,分别执行get(x) ,get(y)完成查询和路径压缩。若二者返回值相同,则二者位于同一列中,输出d[x]+d[y]-1.
当接收到一个M x y指令时,把x的树根作为y树根的子节点,连接的新边的权值应该设为合并之前集合y的大小(根据题意,集合y中的全部战舰都排在集合x之前),因此我们还需要一个size数组在每个树根上记录集合大小。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<bits/stdc++.h> using namespace std; const int N = 3e4 + 5; int fa[N], d[N], size[N]; void init() { for (int i = 0; i<N; i++) fa[i] = i, size[i] = 1; } int get(int x) { if (fa[x] == x) return x; int root = get(fa[x]); d[x] += d[fa[x]]; return fa[x] = root; } void merge(int x, int y) { x = get(x), y = get(y); fa[x] = y; d[x] = size[y]; size[y] += size[x]; } int main() { int t; scanf("%d",&t); init(); while (t--) { char s[2]; int i, j; scanf("%s%d%d",s,&i,&j); if (s[0] == 'M') merge(i, j); else{ if (get(i) != get(j)) cout << -1 << endl; else cout << max(0, abs(d[i] - d[j])-1) << endl; } } return 0; }