推导部分和——带权并查集

news2024/12/26 22:04:11

在这里插入图片描述

题解:

带权并查集

引言: 带权并查集是一种进阶的并查集,通常,结点i的权值等于结点i到根节点的距离,对于带权并查集,有两种操作需要掌握——Merge与Find,涉及到路径压缩与维护权值等技巧。

带权并查集的数据结构

  • 使用顺序存储结构,定义结构体数组,其中a[i]的root代表节点 i 的根节点编号,weight代表它与root号节点之间的距离,也就是权值。

    struct node {
    	int root;ll weight;
    	node() :root(0), weight(0) {}
    }a[100005];
    

Find函数+权值合并

  • 首先,在执行整个并查集算法之前,需要首先初始化每一个结点的根节点编号为它本身,意思就是说,每一个结点在初始状态时都被视为一颗单独的并查集树,即:

    for (int i = 1; i <= n; i++) {
        a[i].root = i;
    }
    
  • 当我们想要去查询一个节点的根节点时,调用find函数:

    • 传入:想要查询的结点编号x

    • 返回: 第x号结点的根节点编号

      //路径压缩+权值合并
      int find(int x) {
       	if (x != a[x].root) {
      		//更新x的根节点
      		int tmp = a[x].root;
      		a[x].root = find(a[x].root);
      		a[x].weight += a[tmp].weight;
      	}
      	return a[x].root;
      }
      
    • 在函数体内:

      • 当结点x的root值为它自己时,即 x == a[x].root 时,直接返回a[x].root
      • 否则,递归查找它根节点的根节点
      • 在递归之前,使用tmp暂存x当前的根节点编号。这是由于在查找的过程中,我们使用了路径压缩的技巧,使a[x].root被赋值为find函数的返回值,但是在后续的计算中,我们又需要使用到这个旧的a[x].root值。
      • 在路径压缩的同时,我们必须要维护权值a[x].weight使其始终等于x号结点到a[x].root号结点的距离
      • 在路径压缩之前,a[x].weight存储的是x到旧的root的距离,但root发生更改后,此时新的权值a[x].weight应该修改为dist(新root,旧root) + dist(旧root,x)才能符合权值的定义,dis(新root, 旧root)将会被递归计算出来,而dis(旧root, x)正是a[x].weight现在存储的值,因此,我们必须要记下旧root的编号才能找到旧root的位置,这也就是tmp发挥的作用。

合并

  • 当我们得到了两个结点之间的距离,并且想要将这两个结点合并,按照并查集的思想,应当先找到他们各自的根节点,然后再将两颗树合并。然而,我们现在所获取的信息并不是根节点的信息,因此我们需要对已知的信息做一个转化:
    • 假设我们现在得到的新的信息是第l-1个节点到第r个节点的距离为w,设第l-1个节点的根节点编号x第r个节点的根节点编号y

    • 首先,我们通过 f i n d ( l − 1 ) find(l - 1) find(l1) f i n d ( r ) find(r) find(r)获得x与y的值,经过find函数内部的权值维护之后,此时,a[l-1].weight和a[r].weight已经分别被修改为l-1到x和r到y的距离了,设它们分别为w1和w2.在这里插入图片描述

    • 通过上图,其实我们很容易就能看出x到y的距离为: w + w 2 − w 1 w+w_2-w1 w+w2w1

    • 在这,我们只需要算出x到y的距离就好了,在后续调用find函数执行路径压缩和权值合并时将会处理掉它,因此,我们合并的操作就是:

      int l, r, w;
      l = read(), r = read(), w = read();
      //x为l-1的根节点,y为r的根节点
      int x = find(l - 1), y = find(r);
      //若l-1与r的根节点不相同
      if (x != y) {
          //将结点x并入y的子树
          a[x].root = y;
          //根据向量的思想计算
          a[x].weight = w + a[r].weight - a[l - 1].weight;
      }
      

查询

写到这里,这个题目已经接近尾声。(此处再次强调a[i].weight的意义是从第i个节点到第a[i].root个节点的距离,接下来要用的) 当我们维护好了一颗带权并查集树之后,那我们查询区间和就只有两种情况:

  • 设区间左端点为l,右端点为r,则
    • 当l与r的根不相同时,则无法查询出l到r的区间和。
    • 当l与r的根相同时,则有 s [ l . . r ] = a [ l − 1 ] . w e i g h t − a [ r ] . w e i g h t s[l.. r]=a[l-1].weight-a[r].weight s[l..r]=a[l1].weighta[r].weight
    • 以图形的方式表达,蓝色部分即为所求的区间和:
      在这里插入图片描述

完整代码:

#include <iostream>
#include <cmath>
#include <algorithm>
#define ll long long
using namespace std;
ll n, m, q;

//带权并查集结点
struct node {
    int root;ll weight;
    node() :root(0), weight(0) {}
}a[100005];
//快读
int read() {
    char ch = getchar(); int res = 0;
    while (ch < '0' || ch>'9') {
        ch = getchar();
    }
    while (!(ch < '0' || ch>'9')) {
        res = res * 10 + (ch - '0');
        ch = getchar();
    }
    return res;
}
//快写
void print(ll x) {
    if (x > 9) {
        print(x / 10);
    }
    putchar(x % 10 + '0');
}

//路径压缩+权值合并
int find(int x) {
    if (x != a[x].root) {
        //更新x的根节点
        int tmp = a[x].root;
        a[x].root = find(a[x].root);
        a[x].weight += a[tmp].weight;
    }
    return a[x].root;
}

int main()
{
    cin >> n >> m >> q;
    for (int i = 1; i <= n; i++) {
        a[i].root = i;
    }
    for (int i = 1; i <= m; i++) {
        int l, r, w;
        l = read(), r = read(), w = read();
        //x为l-1的根节点,y为r的根节点
        int x = find(l - 1), y = find(r);
        //若l-1与r的根节点不相同
        if (x != y) {
            //将结点x并入y的子树
            a[x].root = y;
            //根据向量的思想计算
            a[x].weight = w + a[r].weight - a[l - 1].weight;
        }
    }
    for (int i = 1; i <= q; i++) {
        int l, r; cin >> l >> r;
        if (find(l - 1) != find(r)) {
            puts("UNKNOWN");
        }
        else {
            print(a[l - 1].weight - a[r].weight);
            putchar('\n');
        }
    }
    return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/389885.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

用Python批量重命名文件

案例 今天,我们来整理文件夹中的文件,给某个文件夹下的所有文件重新命名。要求是给所有文件按照修改时间,按顺序在文件名前面加上编号。比如将文件资料.xlsx重命名为1. 资料.xlsx import osdef Get_modify_time(file):return os.path.getmtime(file) #获取文件修改时间path…

vue3的v-model指令

1. 普通input输入框双向绑定 <template><!-- 1. 普通input输入框双向绑定 --><!-- 其实等价于&#xff1a;<input :modelValue"title" update:modelValue"newTitle>titlenewTitle"/> --><input type"text" v-mod…

Junit测试框架

一、简介 Junit框架是一个开源的Java语言单元测试框架&#xff0c;Java方向使用最广泛的单元测试框架&#xff0c;使用Java开发者都应该学习Junit并能掌握单元测试的编写。 对于Junit和Selenium的关系&#xff1a;通俗点来说Selenium如果比喻为灯泡&#xff0c;那么Junit就是电…

【蓝桥杯集训15】求最短路存在负权边——spaf算法(2 / 4)

——SPFA 算法是 Bellman-Ford算法 的队列优化算法的别称 单源最短路&#xff0c;且图中没有负环就可以用spfa 目录 spaf求最短路模板 852. spfa判断负环 341. 最优贸易 - 3305. 作物杂交 - spaf求最短路模板 只有当一个点的前驱结点更新了&#xff0c;该节点才会得到…

操作系统——16.时间片轮转、优先级、多级反馈队列算法

这篇文章我们来看一下进程调度算法中的时间片轮转、优先级、多级反馈队列算法 目录 1.概述 2.时间片轮转调度算法&#xff08;RR&#xff0c;Round-Robin&#xff09; 3.优先级调度算法 4.多级反馈队列调度算法 5.分析对比 1.概述 首先&#xff0c;我们来看一下这篇文章…

计算机网络整理

TCP与UDP 介绍 HTTP&#xff1a;&#xff08;HyperText Transport Protocol&#xff09;是超文本传输协议的缩写&#xff0c;它用于传送WWW方式的数据&#xff0c;关于HTTP协议的详细内容请参考RFC2616。HTTP协议采用了请求/响应模型。 TCP:&#xff08;Transmission Contro…

[YOLO] yolov3、yolov4、yolov5改进

yolov3网络结构图&#xff1a; Yolov3的三个基本组件&#xff1a; &#xff08;1&#xff09;CBL&#xff1a;Yolov3网络结构中的最小组件&#xff0c;由ConvBnLeaky_relu激活函数三者组成。 &#xff08;2&#xff09;Res unit&#xff1a;借鉴Resnet网络中的残差结构&#x…

docker 入门篇

docker为什么会出现&#xff1f; 一款产品&#xff1a;开发---->运维&#xff0c;两套环境&#xff01;应用环境&#xff0c;应用配置&#xff01; 常见问题&#xff1a;我的电脑可以运行&#xff0c;版本更新&#xff0c;导致服务不可用。 环境配置十分的麻烦&#xff0c;…

RL笔记:基于策略迭代求CliffWaking-v0最优解(python实现)

目录 1. 概要 2. 实现 3. 运行结果 1. 概要 CliffWalking-v0是gym库中的一个例子[1]&#xff0c;是从Sutton-RLbook-2020的Example6.6改编而来。不过本文不是关于gym中的CliffWalking-v0如何玩的&#xff0c;而是关于基于策略迭代求该问题最优解的实现例。 CliffWalking-v0的…

Promise-异步回调

1.理解Promise promise是ES6提出的异步编程的新的解决方案&#xff0c;通过链式调用解决ajax回调地狱 从语法上看&#xff0c;promise是一个构造函数&#xff0c;自己身上有all、reject、resolve方法&#xff0c;原型上有then、catch方法 从功能上看&#xff0c;Promise对象用…

BloomFilter原理学习

文章目录BloomFilter简单介绍BloomFilter中的数学知识fpp(误判率/假阳性)的计算k的最小值公式总结编程语言实现golang的实现[已知n, p求m和k](https://github.com/bits-and-blooms/bloom/blob/master/bloom.go#L133)参考BloomFilter简单介绍 BloomFilter我们可能经常听到也在使…

瑞吉外卖——day2

目录 一、新增员工 二、查询分页数据 三、启用、禁用员工账户、编辑员工信息 一、新增员工 点击左上角新增员工 页面如下&#xff1a; 我们随便填数据 &#xff0c;点击保存&#xff0c;请求的地址如下 返回前端可以看到请求方式为Post 在employeeController中编写对应的代…

Elasticsearch:图片相似度搜索的 5 个技术组成部分

作者&#xff1a;Radovan Ondas&#xff0c;Bernhard Suhm 在本系列博文的第一部分中&#xff0c;我们介绍了图像相似度搜索&#xff0c;并回顾了一种可以降低复杂性并便于实施的高级架构。 此博客解释了实现图像相似性搜索应用程序所需的每个组件的基本概念和技术注意事项。 学…

Python采集本地二手房,一键知晓上万房源信息

前言 大家早好、午好、晚好吖 ❤ ~欢迎光临本文章 所以今天教大家用Python来采集本地房源数据&#xff0c;帮助大家筛选好房。 话不多说&#xff0c;让我们开始愉快的旅程吧~ 更多精彩内容、资源皆可点击文章下方名片获取此处跳转 本文涉及知识点 采集基本流程 requests 发送…

【Java】Spring Boot整合WebSocket

【Java】Spring Boot整合WebSocket WebSocket简介 WebSocket是一种协议&#xff0c;用于实现客户端和服务器之间的双向通信。它可以在单个TCP连接上提供全双工通信&#xff0c;避免了HTTP协议中的请求-响应模式&#xff0c;从而实现更高效的数据交换。WebSocket协议最初由HTM…

【计算几何】贝塞尔曲线 B样条曲线简介及其离散化 + Python C++ 代码实现

文章目录一、贝塞尔曲线二、B样条曲线三、Python 代码实现B样条曲线离散化四、C 代码实现B样条曲线离散化4.1 主要代码4.2 其余类4.3 离散效果展示&#xff08;在CAD中展示&#xff09;本文只做简介&#xff0c;关于贝塞尔曲线和B样条曲线的详细介绍&#xff0c;请参考&#xf…

unity UGUI系统梳理 - 基本布局

偷懒了&#xff0c;部分节选unity API API 1、矩形工具 为了便于布局&#xff0c;每个 UI 元素都表示为矩形。可使用工具栏中的__矩形工具 (Rect Tool)__ 在 Scene 视图中操纵此矩形。矩形工具既可用于 Unity 的 2D 功能&#xff0c;也可用于 UI&#xff0c;实际上甚至还可用…

C/C++开发,无可避免的多线程(篇三).协程及其支持库

一、c20的协程概念 在c20标准后&#xff0c;在一些函数中看到co_await、co_yield、co_return这些关键词&#xff0c;这是c20为协程实现设计的运算符。 协程是能暂停执行以在之后恢复的函数。原来我们调用一个功能函数时&#xff0c;只要调用了以后&#xff0c;就要完整执行完该…

【Kettle-佛系总结】

Kettle-佛系总结Kettle-佛系总结1.kettle介绍2.kettle安装3.kettle目录介绍4.kettle核心概念1.转换2.步骤3.跳&#xff08;Hop&#xff09;4.元数据5.数据类型6.并行7.作业5.kettle转换1.输入控件1.csv文件输入2.文本文件输入3.Excel输入4.XML输入5.JSON输入6.表输入2.输出控件…

百度Apollo规划算法——轨迹拼接

百度Apollo规划算法——轨迹拼接引言轨迹拼接1、什么是轨迹拼接&#xff1f;2、为什么要进行轨迹拼接&#xff1f;3、结合Apollo代码为例理解轨迹拼接的细节。参考引言 在apollo的规划算法中&#xff0c;在每一帧规划开始时会调用一个轨迹拼接函数&#xff0c;返回一段拼接轨迹…