当前位置: 首页 > news >正文

树上启发式合并(dsu on tree)

树上启发式合并(dsu on tree)

简单介绍

对于一些在树上统计信息,且对每个节点都有访问其整棵子树的题目,我们可以使用树上启发式合并来简化

启发式合并是一种优雅的暴力,暴力在于他的统计答案是暴力的,而优雅就优雅在他有一个优秀且严格的复杂度

核心思路为:在重链剖分的基础上,对于每个节点,求解完所有轻儿子后再求解重儿子,并将重儿子的答案合并进当前节点中,所以当前节点只需再求解轻儿子即可。

简单伪代码即为:

dfs(x) {for(edge[x]) {int y = to[x];if(y != son[x]) {dfs(y);clear(y);}}dfs(son[x]);// no clear!!cal(x); // get answer in brute force
}

而对于复杂度,提供一个感性证明:

对于每个点 \(x\),被计算答案(进入dfs函数)的次数应为 (根到 \(x\) 的路径上轻边的数量+1),

而根据轻重边的性质,从根到任意点的路径上,轻边个数不超过 \(\log n\)

所以复杂度为 \(O(n\log n)\)

具体实现

以例题 CF600E Lomsat gelral 为例:

题意:一棵有根树,每个点有颜色,若以 \(x\) 为根的子树内颜色 \(c\) 出现的次数最多,则称为主导色(可有多个),计算每个节点为根的子树的主导色编号和。

在暴力解法中,我们可以遍历每一棵子树,在 \(O(n^2)\) 的复杂度内轻易解决这个问题,而使用树上启发式合并后,可以在 \(O(n\log n)\) 的时间内求解

具体实现与伪代码逻辑相同,不再做过多介绍:

// son[x] -> 重儿子
void dfs1(ll x, ll pre) { // initll maxx = 0; siz[x] = 1;for(ll i = hd[x]; i; i = nxt[i]) {ll y = ver[i];if(y == pre) continue;dfs1(y, x);if(maxx < siz[y]) maxx = siz[y], son[x] = y;siz[x] += siz[y];}
}
void dfs3(ll x, ll pre, ll d) { // brute forcecnt[c[x]]++;if(cnt[c[x]] > maxn) maxn = cnt[c[x]], sum = c[x];else if(cnt[c[x]] == maxn) sum += c[x];for(ll i = hd[x]; i; i = nxt[i]) {ll y = ver[i];if(y == pre || y == d) continue;dfs3(y, x, d);}
}
void dfs4(ll x, ll pre) { // clearcnt[c[x]]--;for(ll i = hd[x]; i; i = nxt[i]) {ll y = ver[i];if(y == pre) continue;dfs4(y, x);}
}
void dfs2(ll x, ll pre) { // mainfor(ll i = hd[x]; i; i = nxt[i]) {ll y = ver[i];if(y == pre || y == son[x]) continue;dfs2(y, x);dfs4(y, x);sum = maxn = 0;}if(son[x]) dfs2(son[x], x);dfs3(x, pre, son[x]);ans[x] = sum;
}

在使用该算法是一定要明确好操作顺序后再开始敲代码,尤其是各个dfs函数的顺序和对 \(sum\)\(maxn\) 等变量清空的时机等

应用

暂时按照该题单一道一道做,做完在下面补解法

http://www.sczhlp.com/news/10685/

相关文章:

  • tar 打包报错记录
  • 笛卡尔树知识点+思路
  • Pass 和 Shader的关系
  • 二期鸡熏
  • root密码忘记解决办法
  • 【2025牛客暑期多校训练营9】L Ping Pong
  • 禁止废话
  • 2025.8.12总结 - A
  • 如何优化NebulaGraph的查询性能?
  • nim语言配置nimcache编译缓存
  • 20250811 做题记录
  • 20250812 做题记录
  • [PaperReading] RT-1: ROBOTICS TRANSFORMER FOR REAL-WORLD CONTROL AT SCALE
  • 【03】厦门立林科技——立林科技 嵌入式 校招笔试,题目记录及解析 - 指南
  • JAX快速上手:从NumPy到GPU加速的Python高性能计算库入门教程
  • 数组打印的全量显示设置
  • 8.11总结
  • 8.12总结
  • 2025.08.12 NK9
  • 带修主席树模板
  • 《烛之武退秦师》
  • Admin.NET站在巨人肩膀上的 .NET 通用权限开发框架
  • nebulagraph 查询IO下推总结
  • base_test中的task A,在用例中也写上一个task A,但是这个task A在base_test中调用,实际执行的是用例中的task A,还是base test中的task A
  • LeetCode 面试经典 150_数组/字符串_O(1)时间插入、删除和获取随机元素(12_380_C++_中等)(哈希表) - 教程
  • youwiki大佬的博文
  • 数字化转型别再堆工具了!这款项目管理软件才是破局关键
  • 20250812
  • 数据结构复习第一天(2025/8/12)
  • FWT小记