You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2.6 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

##POJ-1988-Cube Stacking

零、经验总结

  • 家族人数
  • 到根节点距离
  • 利用人数更新到根节点距离

一、题目大意

n个箱子,初始时每个箱子单独为一列;

接下来有p行输入,M, x, y 或者 C, x

对于M,x,y:表示将x箱子所在的一列箱子搬到y所在的一列箱子上;

对于C,x:表示查询箱子x下面有多少个箱子;

二、解析

本题在并查集的基础上,要求输出当前节点的子节点数量。 其实可以转化为带权并查集的问题,求出当前节点的祖先的总子孙数量,减去当前节点到祖先的距离再减一即可。

Up[x]代表x到自己的祖先的距离(也就是上面有多少节点)

sums[x]代表以x为根节点的子孙数量(只对根节点有效,因为在合并时,非根节点的sums值将不再被更新)

s[x]代表x的祖先

三、实现代码

#include <iostream>
#include <cstdio>

using namespace std;
const int N = 30010;
int T;     // 操作数量
int p[N];  // 并查集数组
int sz[N]; // 每个家族中节点数量
int d[N];  // 每个节点到根节点的距离
char op;   // 操作符

// 带权并查集模板
int find(int x) {
    // 点权的理解:每个点到根的距离
    if (p[x] == x) return x; // 自己是老大,返回自己
    int px = find(p[x]);     // 通过自己父亲找老大
    d[x] += d[p[x]];         // 点权在路径压缩过程中,需要加上父亲的点权
    return p[x] = px;        // 路径压缩
}

// 合并并查集
void join(int x, int y) {
    int px = find(x), py = find(y);
    if (px != py) {
        p[py] = px; // px所在的子树放到py所在子树之上
        // 下面这句话是本题相关的修改,重要!!!
        d[py] = sz[px];   // py到px的距离就是px所在子树的结点个数[这个转换太漂亮了]
        sz[px] += sz[py]; // px所在子树结点个数要加上py这一棵树子树
    }
}

int main() {
    // p父结点 sz当前节点子树大小 dis当前节点到根结点距离
    for (int i = 1; i <= N - 10; i++) p[i] = i, sz[i] = 1;
    cin >> T;
    while (T--) {
        cin >> op;
        int x, y;
        if (op == 'M') { // 合并
            cin >> x >> y;
            join(x, y);
        } else {
            cin >> x;
            // 查找某一个点x下边有几个点时只用求出x根结点所在子树的结点个数-x到根结点的距离-1
            printf("%d\n", sz[find(x)] - d[x] - 1);
        }
    }
    return 0;
}