算法竞赛进阶指南---0x41 (并查集) 银河英雄传说

题面

算法竞赛进阶指南---0x41 (并查集) 银河英雄传说_第1张图片
算法竞赛进阶指南---0x41 (并查集) 银河英雄传说_第2张图片
算法竞赛进阶指南---0x41 (并查集) 银河英雄传说_第3张图片

题解

  1. 题中有两种操作方式,一种是合并两个集合,一种是询问集合中元素的关系(距离),那就是并查集,而且是要维护到祖宗节点距离的并查集
  2. 先看查询操作,我们可以用一个d[x] 数组维护 x 到 p[x] 的距离 ,在每次 find(x) 操作之后 +路径压缩,那么x到p[x] 的距离就是 x 到祖宗节点的距离,如果两个战舰x,y在同一个集合中,abs(d[x]-d[y])-1不就是两个战舰之间还有多少个战舰,如果不在就返回-1
  3. 接下来就考虑如何维护 d 数组,也就是合并操作,假设合并两个战舰集合x,y , px=find(x) ,py=find(y),将以px为祖宗节点的集合合并在以py为祖宗节点集合的后面,那么首先px的祖宗节点就变成了py ,就是p[px]=py,然后维护d集合,对于py集合中的距离肯定是没有变化的,对于px集合中,因为祖宗节点换成了py,所以相应的到祖宗节点的距离也应该更新,原来px中d[x]表示x到px的距离,现在只要加上py中元素的个数,就是x到py(新祖宗节点)的距离了,即d[px]+=s[py]; 由于py集合中新加了px集合,所有py集合中元素的个数也应该增加,即s[py]+=s[px];
  4. 重在自己画图理解,具体看代码

代码

#include

using namespace std;
const int N = 3e4 + 10;

int p[N]; //并查集
int s[N]; //集合中战舰的数量
int d[N]; //表示的是x到p[x]的距离,(但是在find路径压缩之后,p[x]其实就是祖宗节点了)

//初始化操作
void init() {
    for (int i = 1; i <= N; i++) {
        p[i] = i;
        s[i] = 1;  //开始时只有自己
        d[i] = 0;  //祖宗节点就是自己,距离为0
    }
}

//返回x的祖宗节点
int find(int x) {
    if (p[x] != x) {
        int root = find(p[x]);
        //距离逐渐迭代增加,路径压缩之后,p[x]指向的就是祖宗节点节点
        d[x] += d[p[x]];
        p[x] = root;
    }
    return p[x];
}


int main() {

    init();
    int t;
    cin >> t;
    char op[2];
    int x, y;
    while (t--) {
        cin >> op >> x >> y;
        int px = find(x), py = find(y);
        if (op[0] == 'M') {
            if(px!=py){
                p[px]=py;
                d[px]+=s[py];
                s[py]+=s[px];
            }
        } else {
            if (px == py) {
                //如果询问的战舰是相同的,中间就不会间隔
                cout << max(0,abs(d[x] - d[y])-1) << endl;
            } else {
                cout << -1 << endl;
            }
        }
    }
    return 0;
}

你可能感兴趣的:(#,并查集,算法,数据结构,并查集)