[NOI2002] 银河英雄传说

题目【带权并查集】

思路

  • 在每次合并的时候,只要更新合并前队头到目前队头的距离就可以了
  • 之后其它的就可以利用它来算出自己到队头的距离。
  • 对于原来的队头,它到队头的距离为0,当将它所在的队列移到另一个队列后面时,它到队头的距离就是排在它前面的飞船数,也就是合并前的num[pa]。

代码

#include <bits/stdc++.h>
using namespace std;

const int N = 30010;
int p[N], d[N], cnt[N];//u到顶点的距离, set u的数目

int find(int x) {
    if(x != p[x]) {
        int px = p[x];
        p[x] = find(p[x]);
        d[x] += d[px];
    }
    return p[x];
}

void merge(int a, int b) {
    int pa=find(a), pb=find(b);
    if(pa == pb) return;
    //pa合到pb
    p[pa] = pb;
    d[pa] = cnt[pb];//pa到pb的距离为原来pb的大小(集合a直接加在集合b的后面)
    cnt[pb] += cnt[pa];
}

int main()
{
    int T;
    cin>>T;
    for(int i=0; i<N; ++i) p[i] = i, cnt[i] = 1;
    
    while(T--) {
        char op[2];
        int a, b;
        scanf("%s%d%d", op, &a, &b);
        if(op[0] == 'M') {
            merge(a,b);
        }else {
            //C
            if(find(a) != find(b)) {
                printf("-1\n");
            }else {
                printf("%d\n", abs(d[a]-d[b])-1);//a与b之间的数目=a到root的距离-b到root的距离-1
            }
        }
    }
    return 0;
}
posted @ 2021-08-18 22:02  misaka-mikoto  阅读(54)  评论(0编辑  收藏  举报