【图论】树上启发式合并

本篇博客参考:

  • Oi Wiki 树上启发式合并
  • 算法学习笔记(86): 树上启发式合并

文章目录

    • 基本概念
    • 代码实现

基本概念

首先,什么是 启发式合并

有人将其称为“优雅的暴力”,启发式合并就是在合并两个部分的时候,将内容少的部分合并至内容多的部分,减少合并的操作时间

树上启发式合并(dsu on tree) 可以被用来解决树上的 离线问题(请注意,必须要是离线问题,因为处理问题的顺序有讲究),特别是可以维护以每个点为根的子树中的信息

一般来说,对于查询以每个点为根的子树中的信息的问题,我们可以用树形dp来处理,但是如果每个点的信息不止一两个数字,而是很庞大的部分(比如说每个点所需要的信息都要多个map来存储),这样使用树形dp的空间复杂度将会非常庞大,而树上启发式合并可以用来解决这样的问题

代码实现

举个例子,比如说我们给出一棵树,树上的每个结点染色,现在我们需要统计以每个结点为根的子树中出现多少种颜色

最暴力的方法就是每个结点跑一次 dfs,用 cnt[] 数组存储每个颜色出现的次数,输出

但是很明显会T的很惨

在这里插入图片描述
这样的树中,我们首先计算2子树的信息,然后计算3子树的信息的时候我们又要把2子树清空,每计算一个新的子树都要把之前计算过的信息清空,根本存不下来信息啊

然后我们考虑一下怎么优化呢,父结点的信息和子结点相关,我们可以用子结点的信息更新父结点的信息,也就是,我们在计算1子树的所有结点信息时,假如4子树是234里最后一个被计算的,那我们算完4子树之后,可以不用清空 cnt 数组,反正我们计算1子树的时候还是要遍历4子树的,将4子树的信息全部保留,再加上前面23子树的信息就可以得到1子树的信息了

这个4子树应该怎么选择呢?换句话说,我们保留哪一个子树的信息不被删除呢?根据启发式合并的思想,保留最庞大的子树信息不动,就可以减少重复计算的次数了

在树链剖分时我们把树中结点最多的子树根结点叫做重子结点,也就是说,在树上启发式合并的过程中,我们需要先计算所有轻子结点的信息(每计算一个轻子结点之后都要删除这个结点对当前答案的影响),最后计算重子结点的信息(保留重子结点对当前答案的影响),然后再计算前面的轻子结点(这一次计算要保留结点对当前答案的影响)

用两遍 dfs 实现

下面是一些变量定义:

  • sz[u] 以 u 为根的子树的结点数量
  • son[u] 结点 u 的重子结点
  • col[u] 结点 u 的颜色
  • L[u] 结点 u 的 dfs 序
  • R[u] 以 u 为根的子树中结点 dfs 序的最大值
  • id[u] L 标号 u 对应的结点编号,有 id[L[u]] == u
  • cnt[u] 颜色 u 的出现次数
  • totcol 目前出现过的颜色个数
void dfs1(int u, int fa) // u: 当前结点  fa: 父结点
{
	L[u] = ++ totdfn; // 更新u的dfs序
	Node[totdfn] = u; // 更新dfs序的映射
	sz[u] = 1; // 初始化子树大小为1
	for (int i = 0; i < g[u].size(); i ++ )
	{
		int j = g[u][i]; // 子结点编号
		if (j == fa) continue;
		dfs1(j, u);
		sz[u] += sz[j]; // 用子结点的sz更新父结点的sz
		if (sz[j] > sz[son[u]]) son[u] = j; // 更新重子结点
	}
	R[u] = totdfn; // 更新当前子树中dfs序的最大值
}

void dfs2(int u, int fa, bool keep) // u: 当前结点  fa: 父结点  keep: 此次遍历计算的答案是否保留
{
	// 计算轻子结点的答案
	for (int i = 0; i < g[u].size(); i ++ )
	{
		int j = g[u][i]; // 子结点编号
		if (j == fa || j == son[u]) continue; // 遇到重子结点或者父结点就跳过
		dfs2(j, u, false); // 继续计算轻子结点的答案且不保留
	}

	if (son[u]) dfs2(son[u], u, true); // 计算重儿子答案并保留计算过程中的数据(用于继承)

	for (int i = 0; i < g[u].size(); i ++ )
	{
		int j = g[u][i]; // 子结点编号
		if (j == fa || j == son[u]) continue; // 遇到重子结点或者父结点就跳过

		// 子树结点的 DFS 序构成一段连续区间,可以直接遍历
		for (int k = L[j]; k <= R[j]; k ++ ) add(id[k]); // 加上轻子结点对答案的贡献
	}
	add(u); // 加上当前子树根结点对答案的贡献
	ans[u] = totcol;
	if (keep == false) // 如果当前计算的答案不保留 就删去
		for (int i = L[u]; i <= R[u]; i ++ ) del(id[i]);
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

如何配置IDEA中的JavaWeb环境(2023最新版)

创建项目 中文版&#xff1a;【文件】-【新建】-【项目】 点击【新建项目】&#xff0c;改好【名称】点击【创建】 右键自己建立的项目-【添加框架支持】&#xff08;英文版是Add Framework Support...&#xff09; 勾选【Web应用程序】-【确定】 配置tomcat 点击编辑配置 点…

【图文详解】Maven Helper插件解决Maven冲突

文章目录 插件问题解决过程 在面试中解决问题的能力和思路是考察的重点&#xff0c;面试官问会问我们有没有解决过maven冲突。以下造了一个maven冲突&#xff0c;手把手教学如何解决Maven冲突。 插件 插件在idea插件中搜索Maven Helper 问题 解决过程 根据上面日志知道是log…

清理磁盘空间 - Win系统

清理磁盘空间 - Win系统 前言系统方案TreeSize FreeSpaceSniffer 前言 我们在使用电脑时经常会出现硬盘空间不足的情况&#xff0c;下文介绍如何清理磁盘空间&#xff0c;包含系统方案、TreeSize Free和SpaceSniffer。清理Window更新等系统文件推荐使用系统方案&#xff0c;清…

在没有推出硬盘的情况下,重启mac电脑,外接移动硬盘无法加载显示?

一、mac磁盘工具显示未装载 1.打开终端&#xff0c;输入 diskutil list查看当前硬盘列表&#xff0c;大多数时候&#xff0c;可以解决。 二、使用命令行装载硬盘 执行上面命令后&#xff0c;仍不起作用&#xff0c;则手动挂载&#xff0c;在命令行输入如下内容&#xff1a; …

TSINGSEE青犀煤矿矿井视频监控与汇聚融合管理视频监管平台建设方案

一、背景需求 随着我国经济的飞速发展&#xff0c;煤炭作为我国的主要能源之一&#xff0c;其开采和利用的重要性不言而喻。然而&#xff0c;煤矿事故频发&#xff0c;不仅造成了巨大的人员伤亡和财产损失&#xff0c;也对社会产生了深远的负面影响。视频监控系统作为实现煤矿智…

【QT】文件流操作(QTextStream/QDataStream)

文本流/数据流&#xff08;二级制格式&#xff09; 文本流 &#xff08;依赖平台&#xff0c;不同平台可能乱码&#xff09;涉及文件编码 #include <QTextStream>操作的都是基础数据类型&#xff1a;int float string //Image Qpoint QRect就不可以操作 需要下面的 …

【案例】蜂窝物联网联合金草生物打造金线莲“工厂”,让金线莲种植更简单

一、项目背景&#xff1a; 金线莲又名金线兰、金草、鸟人参&#xff0c;为兰科开唇兰属植物&#xff0c;是一种传统名贵中药材&#xff0c;对生长的环境要求极其苛刻。传统金线莲种植由于环境不可控&#xff0c;茎腐病、软腐病、猝倒病等病害频发&#xff0c;金线莲产业发展遇到…

使用npm版本管理工具解决npm 的EACCES permissions errors when installing packages globally错误

EACCES错误通常表示“权限被拒绝”&#xff0c;意味着您没有足够的权限来执行某个操作。在计算机领域&#xff0c;尤其是在文件系统和程序安装中&#xff0c;这个错误很常见。以下是可能导致EACCES错误的原因以及相应的解决方法&#xff1a; 文件系统权限&#xff1a;当您尝试…

2024年JavaScript前端框架维护者预测

来自Angular、Next.js、React和Solid的维护者和创建者分享了他们计划在2024年进行的改进 2024年的前端会是什么样子&#xff1f;自从我们打破了我们的水晶球&#xff0c;The New Stack与Angular&#xff0c;Next.js&#xff0c;React和Solid的创建者和维护者讨论了他们2024年的…

UVC 设备框架在 Linux 4.15 内核的演变

1. 概述 发现之前的uvc框架和现在的还是有一些差别的&#xff08;比如从videobuf 过渡到videobuf2&#xff09;&#xff0c;写个blog记录一下&#xff0c;方便以后查询&#xff0c;我的内核版本&#xff1a;Linux 4.15 UVC&#xff08;USB Video Class&#xff09;设备框架是…

ThingsBoard开源物联网平台介绍

1. Thingsboard 简介 ThingsBoard是一个基于Java的开源物联网平台&#xff0c;旨在实现物联网项目的快速开发、管理和扩展。它使用行业标准的物联网协议&#xff08;MQTT、CoAP和HTTP&#xff09;实现设备连接&#xff0c;并支持云和本地部署。ThingsBoard结合了可扩展性、容错…

混合云构建-VPN打通阿里云和Azure云

要在阿里云和Azure云之间通过VPN打通网络,您需要在两边分别设置VPN网关,并配置相应的连接和路由规则以确保两个云环境之间的网络流量可以互通。以下是一个基本的步骤指南: 为了更具体地说明如何在阿里云和Azure之间通过VPN打通网络,我们将通过一个简化的示例来演示整个过程…

【代码随想录 | 数组 05】螺旋矩阵 ||

文章目录 5.螺旋矩阵25.1题目5.2思路 5.螺旋矩阵2 5.1题目 59. 螺旋矩阵 II 给你一个正整数 n &#xff0c;生成一个包含 1 到 n2 所有元素&#xff0c;且元素按顺时针顺序螺旋排列的 n x n 正方形矩阵 matrix 。 示例一&#xff1a; 输入&#xff1a;n 3 输出&#xff…

全景解析 Partisia Blockchain:以用户为中心的全新数字经济网络

在区块链世界中&#xff0c;以比特币、以太坊网络为代表的主流区块链奠定了该领域早期的基础&#xff0c;并让去中心化、点对点、公开透明以及不可逆成为了该领域固有的意识形态。事实上&#xff0c;过于透明正在成为区块链规模性采用的一大障碍&#xff0c;我们看到 90% 以上的…

零知识玩转AVH(2)—— 怎么玩(1)

接前一篇文章&#xff1a;零知识玩转AVH&#xff08;1&#xff09;—— 初次接触 前一篇文章讲了AVH是什么&#xff0c;本文开始&#xff0c;详细AVH具体怎么玩。 由前一篇文章中提到的CSDN工作人员对于活动的说明&#xff0c;可以得出以下信息&#xff1a; 1. 这个任务是分两…

FineReport报表JS实现点击超链打开对话框报表并传参

例如在报表开发中&#xff0c;有如下需求&#xff1a; 点击当前报表中的某些文字&#xff0c;希望弹出另外的报表展示其他信息 &#xff08;即可以通过JS实现点击超链接打开报表对话框&#xff0c;并且可以传递参数到报表对话框中&#xff09;帆软帮助文档参考链接&#xff1a;…

AV1:编码块划分

​AV1是AOM于2018年发布的一代视频编码标准&#xff0c;相比于VP9其编码效率提升30%&#xff0c;相对于H.26X系列标准&#xff0c;AV1完全免去专利费可以自由使用。 AV1和其他视频编码标准类似&#xff0c;也采用基于块的编码架构。当编码器读进一帧图像&#xff0c;首先将其划…

签到提醒小工具:实时屏幕二维码检测+Server酱消息推送

前言 本文做了一个小工具&#xff0c;用来实时检测屏幕中出现的二维码&#xff0c;并通过Server酱发送信息推送到微信。 二维码检测 二维码检测主要通过opencv的detectAndDecode方法&#xff0c;基本用法如下&#xff1a; data, bbox, rectifiedImage detector.detectAndD…

【深度学习笔记】7_3 小批量随机梯度下降

注&#xff1a;本文为《动手学深度学习》开源内容&#xff0c;部分标注了个人理解&#xff0c;仅为个人学习记录&#xff0c;无抄袭搬运意图 7.3 小批量随机梯度下降 在每一次迭代中&#xff0c;梯度下降使用整个训练数据集来计算梯度&#xff0c;因此它有时也被称为批量梯度下…

折扣价和折扣实时转换

背景 : react 项目 问题 : 在折扣数中输入折扣2.333333&#xff0c;中间会多很多0&#xff0c;输入2.222&#xff0c;不能正常输入到第三位 如下图 原因 : toFixed()数字转字符串时可能会导致精度问题 解决思路 : parseFloat来解析浮点数,Number.isFinite判断给定的值是否为有…