洛谷P1196 [NOI2002]银河英雄传说 题解 并查集

题目链接:https://www.luogu.com.cn/problem/P1196

题目大意:
一开始有 \(30000\) 个元素,每个元素组成一个队列。
\(T\) 次操作(\(1 \le T \le 500000\)),操作分类两种类型:

  1. 修改操作“M i j”:将 \(i\) 所处的队列,作为一个整体接至 \(j\) 所处的队列的尾部;
  2. 询问操作“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;
}
posted @ 2020-01-31 19:53  quanjun  阅读(125)  评论(0编辑  收藏  举报