简介

dsu on treedsu没有关系,但是dsu on tree借鉴了dsu的启发式合并的思想。

它是用来解决一类树上的询问问题,一般这种问题有以下特征:

\(1.\)只有对子树的查询;

\(2.\)没有修改。

如果满足以上特征,那么dsu on tree很可能就可以派上用场了。

算法

我们以CF600E Lomsat gelral为例。

Descrption

一棵以\(1\)为根的树有 \(n\) 个结点,每个结点的颜色是\(c_i\),每个颜色有一个编号,求树中每个子树的最多的颜色编号的和。

数据范围\(1\le n\le 10^5, 1\le c_i\le 10^5\)。

Solution

我们考虑暴力怎么写:遍历每一个节点 \(u\) ,然后把子树内的所有颜色暴力统计出来更新答案。

然后消除节点 \(u\) 的贡献,继续递归计算其他点的贡献。

复杂度\(O(n^2)\),显然是很不优美的。

然后dsu on tree就登场了。它也是一个暴力,但是它结合了轻重链剖分,将复杂度降到了\(O(nlogn)\)。

我们先跑一个\(dfs1\),记录节点\(u\)的重儿子\(heavy[u]\)。

接下来:

  • 遍历每一个节点。
  • 递归所有的轻儿子,递归进入时暴力加贡献,递归结束时暴力消除贡献。
  • 递归重儿子,递归进入时暴力加贡献,递归结束时不消除贡献。
  • 统计所有轻儿子的答案,并更新该节点的答案。
  • 删除所有轻儿子对答案的影响。

复杂度\(O(nlogn)\),可以通过本题。

Code

  1. // Author: wlzhouzhuan
  2. #pragma GCC optimize(2)
  3. #pragma GCC optimize(3)
  4. #include <bits/stdc++.h>
  5. using namespace std;
  6. #define ll long long
  7. #define ull unsigned long long
  8. #define rint register int
  9. #define rep(i, l, r) for (rint i = l; i <= r; i++)
  10. #define per(i, l, r) for (rint i = l; i >= r; i--)
  11. #define mset(s, _) memset(s, _, sizeof(s))
  12. #define pb push_back
  13. #define pii pair <int, int>
  14. #define mp(a, b) make_pair(a, b)
  15. inline int read() {
  16. int x = 0, neg = 1; char op = getchar();
  17. while (!isdigit(op)) { if (op == '-') neg = -1; op = getchar(); }
  18. while (isdigit(op)) { x = 10 * x + op - '0'; op = getchar(); }
  19. return neg * x;
  20. }
  21. inline void print(int x) {
  22. if (x < 0) { putchar('-'); x = -x; }
  23. if (x >= 10) print(x / 10);
  24. putchar(x % 10 + '0');
  25. }
  26. const int N = 100005;
  27. int n;
  28. vector <int> adj[N];
  29. void add(int u, int v) { adj[u].pb(v); }
  30. int sz[N], fa[N], heavy[N]; // heavy[u] 表示u的重儿子
  31. void dfs1(int u, int f) {
  32. sz[u] = 1, fa[u] = f;
  33. for (auto v: adj[u]) {
  34. if (v == f) continue;
  35. dfs1(v, u);
  36. sz[u] += sz[v];
  37. if (sz[v] > sz[heavy[u]]) {
  38. heavy[u] = v; // 更新重儿子
  39. }
  40. }
  41. }
  42. long long ans[N], sum;
  43. int cnt[N], col[N], Son, Max;
  44. void change(int u, int val) {
  45. cnt[col[u]] += val;
  46. if (cnt[col[u]] > Max) Max = cnt[col[u]], sum = col[u];
  47. else if (cnt[col[u]] == Max) sum += col[u];
  48. for (auto v: adj[u]) {
  49. // 由于重儿子的信息没有被删去,所以已经统计过了,不能再计算
  50. if (v == fa[u] || v == Son) continue;
  51. change(v, val);
  52. }
  53. }
  54. void dfs2(int u, int keep) {
  55. for (auto v: adj[u]) {
  56. if (v == fa[u] || v == heavy[u]) continue;
  57. dfs2(v, 0); // 遍历u的轻儿子
  58. }
  59. if (heavy[u]) dfs2(heavy[u], 1), Son = heavy[u];
  60. change(u, 1), ans[u] = sum, Son = 0;
  61. if (!keep) change(u, -1), sum = Max = 0;
  62. }
  63. int main() {
  64. n = read();
  65. for (rint i = 1; i <= n; i++) {
  66. col[i] = read();
  67. }
  68. for (rint i = 1; i < n; i++) {
  69. int x = read(), y = read();
  70. add(x, y), add(y, x);
  71. }
  72. int root = 1; // 题目默认1为根
  73. dfs1(root, 0);
  74. dfs2(root, 0);
  75. for (rint i = 1; i <= n; i++) {
  76. printf("%lld ", ans[i]);
  77. }
  78. return 0;
  79. }

[算法学习] dsu on tree的更多相关文章

  1. 【算法】dsu on tree初探

    dsu on tree的本质是树上的启发式合并,它利用启发式合并的思想,可以将O(N^2)的暴力优化成O(NlogN),用于不带修改的子树信息查询. 具体如何实现呢?对于一个节点,继承它重儿子的信息, ...

  2. Note -「Dsu On Tree」学习笔记

    前置芝士 树连剖分及其思想,以及优化时间复杂度的原理. 讲个笑话这个东西其实和 Dsu(并查集)没什么关系. 算法本身 Dsu On Tree,一下简称 DOT,常用于解决子树间的信息合并问题. 其实 ...

  3. dsu on tree详解

    这个算法还是挺人性化的,没有什么难度 就是可能看起来有点晕什么的. 大体 思想是 利用重链刨分来优化子树内部的查询. 考虑一个问题要对每个子树都要询问一次.我们暴力显然是\(n^2\)的. 考虑一下优 ...

  4. dsu on tree入门

    先瞎扯几句 说起来我跟这个算法好像还有很深的渊源呢qwq.当时在学业水平考试的考场上,题目都做完了不会做,于是开始xjb出题.突然我想到这么一个题 看起来好像很可做的样子,然而直到考试完我都只想出来一 ...

  5. 【CodeForces】741 D. Arpa’s letter-marked tree and Mehrdad’s Dokhtar-kosh paths(dsu on tree)

    [题意]给定n个点的树,每条边有一个小写字母a~v,求每棵子树内的最长回文路径,回文路径定义为路径上所有字母存在一种排列为回文串.n<=5*10^5. [算法]dsu on tree [题解]这 ...

  6. 【CodeForces】600 E. Lomsat gelral (dsu on tree)

    [题目]E. Lomsat gelral [题意]给定n个点的树,1为根,每个点有一种颜色ci,一种颜色占领一棵子树当且仅当子树内没有颜色的出现次数超过它,求n个答案——每棵子树的占领颜色的编号和Σc ...

  7. dsu on tree (树上启发式合并) 详解

    一直都没出过算法详解,昨天心血来潮想写一篇,于是 dsu on tree 它来了 1.前置技能 1.链式前向星(vector 建图) 2.dfs 建树 3.剖分轻重链,轻重儿子 重儿子 一个结点的所有 ...

  8. [dsu on tree]【学习笔记】

    十几天前看到zyf2000发过关于这个的题目的Blog, 今天终于去学习了一下 Codeforces原文链接 dsu on tree 简介 我也不清楚dsu是什么的英文缩写... 就像是树上的启发式合 ...

  9. [学习笔记]Dsu On Tree

    [dsu on tree][学习笔记] - Candy? - 博客园 题单: 也称:树上启发式合并 可以解决绝大部分不带修改的离线询问的子树查询问题 流程: 1.重链剖分找重儿子 2.sol:全局用桶 ...

随机推荐

  1. github 上有趣又实用的前端项目(持续更新,欢迎补充)

    github 上有趣又实用的前端项目(持续更新,欢迎补充) 1. reveal.js: 幻灯片展示框架 一个专门用来做 HTML 幻灯片的框架,支持 HTML 和 Markdown 语法. githu ...

  2. 何使用派生类指针指向基类,即downcast向下转型?

    基类指针指向派生类,我们已经很熟了.假如我们想用派生类反过来指向基类,就需要有两个要求:1)马克-to-win:基类指针开始时指向派生类,2)我们还需要清清楚楚的转型一下. if you want t ...

  3. Android开发小经验

    1. TextView中的getTextSize返回值是以像素(px)为单位的, 而setTextSize()是以sp为单位的. 所以如果直接用返回的值来设置会出错,解决办法是 用setTextSiz ...

  4. 彻底理解volatile关键字

    1. volatile简介 在上一篇文章中我们深入理解了java关键字,我们知道在java中还有一大神器就是关键volatile,可以说是和synchronized各领风骚,其中奥妙,我们来共同探讨下 ...

  5. box-shadow 阴影的高级用法,多个阴影叠加

    box-shadow的这些用法你知道吗? $shadowH: ''; @for $i from 1 through 12 { $shadowH: #{$shadowH}, 0 ($i * 30px) ...

  6. BootstrapBlazor实战 Chart 图表使用(1)

    BootstrapBlazor组件 Chart 图表介绍 通过给定数据,绘画各种图表的组件 本文主要介绍三种图表使用:折线图,柱状图,饼图 1.新建工程 新建工程b06chart,使用 nuget.o ...

  7. AcWing 1220. 生命之树

    题目链接 题目描述: 在X森林里,上帝创建了生命之树. 他给每棵树的每个节点(叶子也称为一个节点)上,都标了一个整数,代表这个点的和谐值. 上帝要在这棵树内选出一个非空节点集 S,使得对于 S 中的任 ...

  8. 关于allegro找不到env文件解决方法

    使用allegro的友人时对于env文件并不陌生.在我们设计的过程中经常使用env文件设置快捷键从而达到快速拉线的目的.但是新安装的allegro软件中会找不到env文件,因为今天自己碰到了这件事,并 ...

  9. centos7.3 安装oracle 详细过程

    centos7.3安装oracle详细过程1.下载Oracle安装包:linux.x64_11gR2_database_1of2.zip 和 linux.x64_11gR2_database_2of2 ...

  10. Oracle双字段约束

    Oracle里有unique约束,意思是该字段唯一. 但如果是两个字段呢? 比如说一个会员等级表 ID NAME POINT DISCOUNT PRIVILEGE MID 1019 普通会员 0 10 ...