并查集的理解,附练习题

本文深入探讨并查集的概念,包括如何判环、合并后集合数量的判断,以及带权并查集的理解。通过一系列练习题,如HDU3038、238. 银河英雄传说等,解释了带权并查集和种类并查集在解决实际问题中的应用,如区间合法性检查、奇偶游戏和食物链问题。同时,还介绍了并查集在求解联通块价值、接雨水II等问题中的策略,如优先队列方法和树上两点间最大值与最小值差值的统计。
摘要由CSDN通过智能技术生成

模板

首先贴上模板:

struct DSU {
   
    vector<int> fa, dis, sz;
    int n, comp_cnt;
    
    DSU(int _n) : n(_n), comp_cnt(_n), fa(_n), dis(_n, 0), sz(_n, 1) {
   
        iota(fa.begin(), fa.end(), 0);
    }
    
    int find(int x) {
   
        if (x == fa[x]) return x;
        int t = fa[x];
        fa[x] = find(fa[x]);
        dis[x] += dis[t];
        return fa[x];
    }
    
    void unite(int x, int y) {
   
        x = find(x);
        y = find(y);
        if (x != y) {
   
            if (sz[x] < sz[y]) {
   
                swap(x, y);
            }
            fa[y] = x;
            sz[x] += sz[y];
        }
    }
    
    void unite_dis(int x, int y) {
   
        // 把x接到y后面
        x = find(x);
        y = find(y);
        if (x != y) {
   
            fa[x] = y;
            dis[x] += sz[y];
            sz[y] += sz[x];
        }
    }
    
    bool connected(int x, int y) {
   
        return find(x) == find(y);
    }
};

普通并查集一些应用

并查集判环

形成环等价于新加一条边时,这条边的两个点是否已经都在同一个并查集里了,若新添加边的两个端点都已在同一个集合内,则连通它构成一个环。

并查集合并完后判断有几个集合

int num_set = 0;
for (int i = 0; i < n; i++) {
   
	if (fa[i] == i) {
   
		num_set++;
	}
}

带权并查集的理解

普通并查集只能简单的维护某几个元素在同一个集合内,但是对这几个元素之间的关系没有记录。带权并查集就是为了记录集合内各个元素之间的关系而产生的,其主要通过记录当前元素与根的某种关系,这种关系可以是距离,也可以是奇偶性,主要根据每个题来定。
带权并查集在合并时本质上是对向量的一种操作,这样才能完成合并时计算出根节点连接时边上的权值是多少。在合并的时候,本质上是一种元素之间关系的传递。
在这里插入图片描述
例如上图,现在有两个集合,现在告诉 1 1 1 4 4 4之间的关系为 x x x,那么需要将两个集合合二为一,那么 15 ⃗ \vec{15} 15 之间就有了两条路径,分别是:
13 ⃗ + 35 ⃗ (1) \vec{13}+\vec{35} \tag{1} 13 +35 (1)
14 ⃗ + 45 ⃗ (2) \vec{14}+\vec{45} \tag{2} 14 +45 (2)
v a l [ i ] val[i] val[i]代表了 i i i节点到根节点的权值。
( 1 ) = = ( 2 ) (1)==(2) (1)==(2)得到 v a l [ 1 ] ( 即 13 ⃗ ) + v a l [ 3 ] = v a l [ 4 ] + x val[1](即\vec{13})+val[3]=val[4]+x val[1](13 )+val[3]=val[4]+x,即 v a l [ 3 ] = v a l [ 4 ] + x + v a l [ 1 ] val[3]=val[4]+x+val[1] val[3]=val[4]+x+val[1]
若要求同一个集合内某两个元素之间的权值关系,也是一样。
在这里插入图片描述
比如这里只有一个集合,要求集合内 1 1 1 4 4 4的权值关系,即 v a l [ 1 ] = x + v a l [ 4 ] val[1]=x+val[4] val[1]=x+val[4],所以 x = v a l [ 1 ] − v a l [ 4 ] x=val[1]-val[4] x=val[1]val[4]

练习题

HDU3038 - How Many Answers Are Wrong

HDU3038
给你一系列区间和,判断给出的区间中有几个是不合法的。
在这里插入图片描述
合并时 v a l [ a ] + v a l [ r a ] = s + v a l [ b ] val[a]+val[ra]=s+val[b] val[a]+val[ra]=s+val[b]
检查时 s + v a l [ b ] = = v a l [ a ] s+val[b]==val[a] s+val[b]==val[a];

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

const int N = 2e5 + 10;
int fa[N], val[N];

int find(int x) {
   
	if (fa[x] == x) return x;
	int t = fa[x];
	fa[x] = find(fa[x]);
	val[x] += val[t];
	return fa[x];
}

int main() {
   
	cin.tie(0);
	ios_base::sync_with_stdio(false);
	int n, m;
	while (cin >> n >> m) {
   
		for (int i = 0; i <= n; i++) fa[i] = i;
		memset(val, 0, sizeof(val));
		
		int cnt = 0;
		for (int i = 0; i < m; i++
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值