【转载】启发式合并

news/2024/11/19 12:11:34/文章来源:https://www.cnblogs.com/gutongxing/p/18386984

https://zhuanlan.zhihu.com/p/560661911

数据结构学习笔记(8) 启发式合并

启发式合并是用来解决子树中的统计问题

在codeforces上叫做dsu on tree(树上启发式合并)。这里我们主要是来讲在树上进行启发式合并。

实际上之前我有讲过启发式合并严格鸽:启发式合并 看似暴力实则很快的算法

还有利用启发式合并的并查集严格鸽:ACM——可撤销并查集教程

但是没有讲过树上启发式合并。

我们一般需要维护一个 sub[u]sub[u] ,表示以 uu 为根的子树中的点。

下图中 sub[3]=[3,6,7,8,9]sub[3] = [3,6,7,8,9]

但是如果暴力维护每个 sub[u]sub[u] 肯定是会爆炸的。

但是


严格鸽:启发式合并 看似暴力实则很快的算法

启发式合并就是在合并的时候将size小的那个集合合并到size大的那个集合里面。

比如[1,2,3] 和 [3,5,6,7] 合并,选择遍历前者来把元素放入后者。

void merge(vector<int>& a, vector<int>& b) {if (a.size() > b.size()) {for (int x : b)a.push_back(x);}else {for (int x : a)b.push_back(x);}
}

初看上可能感觉这就是个暴力。但是我们分析一下每个元素被push_back()了多少次。

一个集合中的元素被放入另一个集合中会被push_back()一次。但是这个元素所在的集合的大小至少扩大了一倍。所以一个元素最多被push_back()O(log(N))O(log(N)) 次。


也就是用启发式合并,总的时间复杂度O(nlogn)\rm O(nlogn)

对于 sub[u]\rm sub[u] ,可以从其子节点 v\rm v ,利用启发式合并进行转移。

这里考虑下实现,一般的做法是,我们开一个

vector<int>sub[N]

mxson\rm mx_{son} 表示子树最大的子节点。

然后我们把其它的子节点中的 subsub 都放到这个 mxsonmx_{son} 中。

然后把这个 mxsonmx_{son} 复制给 uu ,但是因为有个复制操作,所以需要。

id[u]id[u] 表示 uu 被映射到了哪个位置。

这样有以下代码

vector<int>sub[N];
void dfs(int u, int fa) {id[u] = ++tot;int mx_son = -1, mx_sz = 0;for (int v : g[u]) {if (v == fa)continue;dfs(v, u);if (sub[id[v]].size() > mx_sz) {mx_sz = sub[id[v]].size();mx_son = v;}}if (mx_son != -1)id[u] = id[mx_son];//复制操作
    for (int v : g[u]) {if (v == fa)continue;if (v == mx_son)continue;for (int son : sub[id[v]])sub[id[u]].push_back(son);}sub[id[u]].push_back(u);
}

当然优化复制操作可以用c++11的move。

其实这样就可以直接做题了

题目链接:

Problem - E - Codeforces

题意:

题意来源洛谷


做法:

我们在记录子树出现了哪些节点之外,还需要记录每种颜色出现的次数。

所以我们直接套一个结构体

struct node {int mx_cnt = 0;//最多的出现次数
    ll mx_sum = 0;//出现次数最多的颜色的编号和
    map<int, int>cnt;vector<int>list;void add(int u) {cnt[c[u]]++;if (cnt[c[u]] > mx_cnt)mx_cnt = cnt[c[u]], mx_sum = c[u];else if (cnt[c[u]] == mx_cnt)mx_sum += c[u];list.push_back(u);}int size() { return list.size(); }
}sub[N];

这里我们选择直接套一个map,复杂度为 O(nlog2n)\rm O(nlog^2n) , 10510^5 的数据完全够用。

这样我们套一下上面的代码,就可以愉快的做出本题了。

code

const int N = 1e5 + 5;
int n, c[N], id[N], tot = 0;
struct node {int mx_cnt = 0;//最多的出现次数
    ll mx_sum = 0;//出现次数最多的颜色的编号和
    map<int, int>cnt;vector<int>list;void add(int u) {cnt[c[u]]++;if (cnt[c[u]] > mx_cnt)mx_cnt = cnt[c[u]], mx_sum = c[u];else if (cnt[c[u]] == mx_cnt)mx_sum += c[u];list.push_back(u);}int size() { return list.size(); }
}sub[N];
ll ans[N];
vector<int>g[N];
void dfs(int u, int fa) {id[u] = ++tot;int mx_son = -1, mx_sz = 0;for (int v : g[u]) {if (v == fa)continue;dfs(v, u);if (sub[id[v]].size() > mx_sz) {mx_sz = sub[id[v]].size();mx_son = v;}}if(mx_son!=-1)id[u] = id[mx_son];for (int v : g[u]) {if (v == fa)continue;if (v == mx_son)continue;for (int son : sub[id[v]].list)sub[id[u]].add(son);}sub[id[u]].add(u);ans[u] = sub[id[u]].mx_sum;
}
void slove() {cin >> n;for (int i = 1; i <= n; i++)cin >> c[i];for (int i = 1; i <= n - 1; i++) {int u, v; cin >> u >> v;g[u].push_back(v);g[v].push_back(u);}dfs(1, 0);for (int i = 1; i <= n; i++)cout << ans[i] << " ";cout << endl;
}

不过这个是一个比较裸的启发式合并的题目,大家可以做做下面两道题目练习。

严格鸽:Codeforces Round #760 (Div. 3) G(离线/并查集/数据结构)

严格鸽:Educational Codeforces Round 132 C(贪心) D E(启发式合并 + 懒标记)



除了启发式合并,我们还可以用线段树合并来解决此类问题。

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

利用通义灵码实现我的第一次开源贡献

作者:重庆邮电大学计算机学院李逸雄 结缘开源 最早了解开源是从学校的兴趣组织开始的。2023 年 10 月 21 日,openSUSE 亚洲峰会在我们学校召开,这次会议汇聚了许多来自 openSUSE 社区贡献者以及对开源感兴趣的爱好者们。我第一次知道有这么多志同道合的爱好者在进行开源贡献…

腾讯云域名托管到 cloudflare

cloudflare https://dash.cloudflare.com/ 腾讯云域名列表 https://console.cloud.tencent.com/domain/all-domain/all 先进入 腾讯云列表,点击自己已购买的域名点击修改; https://console.cloud.tencent.com/domain/all-domain/all修改为cloudflare提供的, 如下: 进入 htt…

中间件实时监控,运维难题一站解决

智和信通方案通过构建对Tomcat、Jboss、WebLogic等中间件的关键指标的监控,实现对中间件性能和资源的实时追踪,识别并解决影响中间件性能的问题,保障中间件的高性能及高可用性,更全面地支撑业务及应用的稳定、持续运行,提升用户体验。 中间件是介于操作系统和在其上…

【性能优化+数据库】读写分离方案

读写分离是一种常见的优化方案,旨在通过将读操作、和写操作分开,如下图所示:大致的原理,如下: 【主库(Master)】:负责处理所有的写操作(比如:插入、更新、删除......)、和写操作相关的事务;【从库(Slave)】:负责处理读操作(查询),通过主从复制机制从主库同步…

【性能优化+数据库】读写分离

读写分离是一种常见的优化方案,旨在通过将读操作、和写操作分开,如下图所示:大致的原理,如下: 【主库(Master)】:负责处理所有的写操作(比如:插入、更新、删除......)、和写操作相关的事务;【从库(Slave)】:负责处理读操作(查询),通过主从复制机制从主库同步…

VL25 输入序列连续的序列检测

这个题目的意思是输入是单bit脉冲,然后当8个周期的脉冲序列符合给定的参数值则match输出1; 因此肯定需要一共8位的寄存器存储总共8个a的输入脉冲 此外由于是从左向右匹配,因此每个周期输入的a要从寄存器最低位输入,从右向左移位(temp_a<={temp_a[6:0],a};),这样才是输…

电商领域的新引擎:API接口的革命性应用

​在数字化转型的大潮中,电商行业正经历着前所未有的变革。API接口,作为连接不同系统和服务的桥梁,正在成为电商领域的新引擎。本文将探讨电商API接口如何助力企业释放数据潜力,驱动业务增长。 一、电商API接口:连接的力量 API(Application Programming Interface)接口是…

HDMI详解

HDMI详解 摘要 本文详细介绍了HDMI接口的定义、不同类型的接口、HDMI脚位功能、版本间的区别,重点探讨了电路设计,包括电源、HPD检测、I2C通信、数据时钟、ARC/eARC音频回传以及CEC消费电子控制等内容,为HDMI产品设计者提供了实用指南。 HDMI的定义 HDMI是高清多媒体接口(Hi…

NSIS入门笔记

1. 基本介绍 NSIS (Nullsoft Scriptable Install System)是一个专业的开源系统,用于创建Windows安装程序。平时工作中想要把exe文件打包,并且在不同系统之间安装,可以使用NSIS,本文使用版本为3.08。 2. 下载地址 可以通过搜索引擎搜索NSIS,推荐安全下载链接:https://nsis…

NSIS新手入门

1. 基本介绍 NSIS (Nullsoft Scriptable Install System)是一个专业的开源系统,用于创建Windows安装程序。平时工作中想要把exe文件打包,并且在不同系统之间安装,可以使用NSIS,本文使用版本为3.08。 2. 下载地址 可以通过搜索引擎搜索NSIS,推荐安全下载链接:https://nsis…

MDS100-16-16-ASEMI三相整流模块MDS100-16

MDS100-16-16-ASEMI三相整流模块MDS100-16编辑:ll MDS100-16-16-ASEMI三相整流模块MDS100-16 型号:MDS100-16 品牌:ASEMI 封装:M18 批号:2024+ 类型:整流模块 电流:100A 电压:1600V 安装方式:直插式封装 特性:大功率、整流模块 产品引线数量:5 产品内部芯片个数:6 …

[JS] ArrayBuffer、DataView和TypedArray

本文介绍了 JavaScript 中 ArrayBuffer 和 TypedArray 的应用。ArrayBuffer 用于预分配内存,存储二进制数据,但需通过 DataView 或 TypedArray 视图来读写。TypedArray 是定型数组,用于高效处理特定数据类型。DataView 则提供了更灵活的方式,适合在二进制文件处理、WebSock…