带删除的并查集

Almost Union-Find
支持三种操作

  1. 合并 x x x y y y所在的集合
  2. x x x移到 y y y所在的集合
  3. x x x所在的集合的元素个数和元素之和

操作1和3是基本的并查集的操作.

关键在于操作 2 2 2:
若使用朴素的并查集,把节点 1 1 1合并到 3 3 3所在的集合,会同时也把1的儿子节点 2 2 2也移动过去,这不是我们想要的。(这里借用luogu题解区的图片)
在这里插入图片描述

我们可以通过给每个元素设置一个虚拟父节点,每次移动的时候操作的是实际的节点,而不是父节点即可完成。因为这个时候操作的节点都是叶子节点,不会有副作用。
在这里插入图片描述

代码中的下标从 0 0 0开始,因此在输入输出的时候有特殊处理。操作2对应于 D S U DSU DSU m o v e move move函数.

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

#ifdef LOCAL
#include "debug.h"
#else
#define debug(...) 42
#endif

typedef long long LL;

struct DSU {
    std::vector<int> f, siz;
	// 集合内的元素和
	std::vector<long long> s;
    
    DSU() {}
    DSU(int n) {
        init(n);
    }
    
    void init(int n) {
		f.resize(n * 2);
		siz.resize(n * 2);
		s.resize(n * 2);
		// 每个点有一个虚点,对应的编号为: i+n, 虚点的父亲指向自己
		for (int i = n; i < n + n; i++) {
			f[i - n] = i;
			f[i] = i;
			siz[i] = 1;
			s[i] = i - n;
		}
    }
    
    int find(int x) {
        while (x != f[x]) {
            x = f[x] = f[f[x]];
        }
        return x;
    }
    
    bool same(int x, int y) {
        return find(x) == find(y);
    }
    
    bool merge(int x, int y) {
        x = find(x);
        y = find(y);
        if (x == y) {
            return false;
        }
        siz[x] += siz[y];
        s[x] += s[y];
		f[y] = x;
        return true;
    }
	
	// 把y添加到x的集合
	bool move(int x, int y) {
		int u = find(x), v = find(y);
		if (u == v) {
			return false;
		}
		// 注意这里的变量,y是叶子节点
		f[y] = u;
		siz[u]++, siz[v]--;
		s[u] += y, s[v] -= y;
		return true;
	}
    
    int size(int x) {
        return siz[find(x)];
    }
	
	int sum(int x) {
		return s[find(x)];
	}
};


int main() {
	int n, m;
	while (cin >> n >> m)
	{
		DSU dsu(n);
		while (m--) {
			int op, p, q;
			cin >> op;
			if (op == 1) {
				cin >> p >> q;
				dsu.merge(p - 1, q - 1);
			} else if (op == 2) {
				cin >> p >> q;
				dsu.move(q - 1, p - 1);
			} else {
				cin >> p;
				cout << dsu.size(p - 1) << " " << dsu.sum(p - 1) + dsu.size(p - 1) << endl;
			}
		}
	}
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值