洛谷P1196 [NOI2002]银河英雄传说 题解 并查集
题目链接:https://www.luogu.com.cn/problem/P1196
题目大意:
一开始有 \(30000\) 个元素,每个元素组成一个队列。
有 \(T\) 次操作(\(1 \le T \le 500000\)),操作分类两种类型:
- 修改操作“M i j”:将 \(i\) 所处的队列,作为一个整体接至 \(j\) 所处的队列的尾部;
- 询问操作“C i j”:询问 \(i\) 和 \(j\) 是否处于同一队列中,如果存在同一队列中,那么请问 \(i\) 和 \(j\) 之间隔了多少个元素?
解题思路:
多开一个dist数组,\(dist[i]\) 用于表示 \(i\) 距离当前队首元素(集合的根节点)的距离,然后路径压缩的时候更新一下 \(dist[i]\) 即可。
实现代码如下:
#include <bits/stdc++.h>
using namespace std;
const int n = 30000;
int f[n+1], dist[n+1], sz[n+1], T, i, j;
char op[2];
void init() {
for (int i = 1; i <= n; i ++) {
f[i] = i; // f[i]表示i的根节点
dist[i] = 0; // dist[i]表示i距根节点的距离
sz[i] = 1; // dist[i]表示i为根节点的集合中元素个数
}
}
int func_find(int x) {
int p = f[x];
if (x == p) return x;
f[x] = func_find(p);
dist[x] += dist[p];
return f[x];
}
void func_union(int x, int y) { // 将x所在的队列合并到y所在的队列后面
int a = func_find(x), b = func_find(y);
dist[a] = sz[b];
sz[b] += sz[a];
f[a] = b;
}
int main() {
init();
scanf("%d", &T);
while (T --) {
scanf("%s%d%d", op, &i, &j);
if (op[0] == 'M') {
func_union(i, j);
}
else {
if (func_find(i) != func_find(j)) puts("-1");
else printf("%d\n", abs(dist[i]-dist[j])-1);
}
}
return 0;
}