带权并查集

题目

并查集相信大家都不陌生,能够以极低的时间复杂度进行区间合并和区间查询,而带权并查集就是在此基础上新增了查询和维护节点到根节点距离的功能,注意此处所说的距离不是并查集树形数据结构里节点之间的距离,而是题目里面描述和规定的距离。

比如对于这道带权并查集的经典例题洛谷P1196 银河英雄传说,它的题意如下,在这道题中距离的定义就是两个战舰之间所间隔的战舰数。

在并查集里面,为了减小时间花费,我们把一个集合里的所有结点都直接挂在根节点下面。在带权并查集中我们同样是这种做法,对于一一对父子结点,它们所代表的两艘战舰直接的距离可能是任意值。
在这里插入图片描述

代码

这是例题的代码,一切都写在注释里面。

#include <iostream>
using namespace std;
const int N = 300010;
// Node结构体代表一艘战舰
struct Node{
    Node* pre;   	// 当前节点的父节点
    int preDist; 	// 当前节点距离pre节点的距离
    int size;		// 如果当前节点为根节点,那么size代表这个集合的节点数量
}nodes[N];

// find函数的作用有三:
// 1. 把node节点的父节点赋值为根节点
// 2. 更新node节点距父节点的距离
// 3. 返回node元素的父节点(根节点)
Node* find(Node* node){
    if(node->pre != node){
        Node* root = find(node->pre);
        // 因为刚刚对父节点调用了find函数,所以此时父节点直接挂在根节点下面
		// 所以当前节点的predist加上父节点的predist等于当前节点到根节点距离
        node->preDist += node->pre->preDist; 
        node->pre = root;
    }  
    return node->pre;
}

// 把X元素所在集合,合并到Y元素所在集合里面
void merge(Node* nodeX, Node* nodeY){
	Node* rootx = find(nodeX);
    Node* rooty = find(nodeY);
    if(rootx != rooty){
    	// x根元素到父节点的距离等于y元素所在集合的节点数量
        rootx->preDist = rooty->size;
        // 合并之后要更新一下y所在集合的节点数量
        rooty->size += rootx->size;
        rootx->pre = rooty;
    }
}
int main(){
    ios::sync_with_stdio(0);
    cin.tie(0);
    int t;
    cin >> t;
    for(int i = 1; i <= N; i++){
        nodes[i].pre = nodes + i;
        nodes[i].size = 1;
    }
    while(t--){
        string s;
        int x, y;
        cin >> s >> x >> y;
        if(s == "M"){
        	// 合并集合
            merge(nodes + x, nodes + y);
        }
        else {
            Node* rootx = find(nodes + x);
            Node* rooty = find(nodes + y);
            if(rootx != rooty){
                cout << -1 << endl;
            }
            else {
            	// 因为执行过find函数,所以x,y都是直接挂在根节点下面的
                cout << max(abs(nodes[x].preDist - nodes[y].preDist) - 1, 0) << endl;
            }
        }
    }

}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值