考试遇到一道题:

有一棵n个点的有根树,每个点有一个颜色,每次询问给定一个点\(u\)和一个数\(k\),询问\(u\)子是多少个不同颜色节点的\(k\)级祖先。n<=500000。

显然对每一层建主席树可行,但还有更优雅的一种做法——\(DSU\)

所谓\(DSU\),是一类处理子树信息的问题的通解(\(O(n\log n\))

其主要过程是树剖后,沿重儿子向下,优先统计轻儿子,并在统计结束后删除对轻儿子统计信息,最后删除对重儿子统计信息。

参考代码

  1. #include <cstdio>
  2. #include <cstring>
  3. #include <iostream>
  4. #include <algorithm>
  5. #include <cmath>
  6. #include <numeric>
  7. #define R(a,b,c) for(register int a = (b); a <= (c); ++a)
  8. #define nR(a,b,c) for(register int a = (b); a >= (c); --a)
  9. #define Swap(a,b) ((a) ^= (b) ^= (a) ^= (b))
  10. #define MP make_pair
  11. #ifdef QWQ
  12. #define D_e_Line printf("\n--------\n")
  13. #define C_e(x) cout << (#x) << " : " << x << endl
  14. #define D_e(x) cerr << (#x) << " : " << x << endl
  15. #define Pause() system("pause")
  16. #define FileOpen() freopen("in.txt", "r", stdin)
  17. #define FileSave() freopen("out.txt", "w", stdout)
  18. #include <assert.h>
  19. #define TIME() fprint(stderr, "TIME : %.3lfms\n", (double)clock() / CLOCKS_PER_SEC)
  20. #define dbg(...) fprintf(stderr, __VA_ARGS__)
  21. #else
  22. #define D_e_Line
  23. #define D_e(x)
  24. #define C_e(x)
  25. #define Pause
  26. #define FileOpen()
  27. #define FileSave()
  28. #define TIME()
  29. #define dbg(...)
  30. #endif
  31. struct FastIO {
  32. template<typename ATP> inline FastIO& operator >> (ATP &x) {
  33. x = 0; int f = 1; char c;
  34. for(c = getchar(); c < '0' || c > '9'; c = getchar()) if(c == '-') f = -1;
  35. while(c >= '0' && c <= '9') x = x * 10 + (c ^ '0'), c = getchar();
  36. if(f == -1) x = -x;
  37. return *this;
  38. }
  39. } io;
  40. using namespace std;
  41. template<typename ATP> inline ATP Max(ATP x, ATP y) {
  42. return x > y ? x : y;
  43. }
  44. template<typename ATP> inline ATP Min(ATP x, ATP y) {
  45. return x < y ? x : y;
  46. }
  47. template<typename ATP> inline ATP Abs(ATP x) {
  48. return x < 0 ? -x : x;
  49. }
  50. #include <vector>
  51. #include <set>
  52. const int N = 1e5 + 7;
  53. const int base = 122777;
  54. char str[27];
  55. inline unsigned long long HashVal(char *str) {
  56. int len = strlen(str + 1);
  57. unsigned long long s = 1;
  58. R(i,1,len)
  59. s = s * base + str[i] - 'a';
  60. return s;
  61. }
  62. struct Edge {
  63. int nxt, pre;
  64. } e[N];
  65. int head[N], cntEdge;
  66. inline void add(int u, int v) {
  67. e[++cntEdge] = (Edge){ head[u], v}, head[u] = cntEdge;
  68. }
  69. struct Ques {
  70. int K, id;
  71. Ques() {}
  72. Ques(int _K, int _id) : K(_K), id(_id) {}
  73. };
  74. int ans[N];
  75. vector<Ques> q[N];
  76. set<int> tot[N];
  77. unsigned long long val[N];
  78. int son[N], dep[N], fa[N], siz[N];
  79. inline void DFS_First(int u, int father) {
  80. dep[u] = dep[father] + 1, fa[u] = father, siz[u] = 1;
  81. for(register int i = head[u]; i; i = e[i].nxt){
  82. int v = e[i].pre;
  83. DFS_First(v, u);
  84. siz[u] += siz[v];
  85. if(!son[u] || siz[v] > siz[son[u]]) son[u] = v;
  86. }
  87. }
  88. bool big[N];
  89. inline void Clear(int u, int father) {
  90. tot[dep[u]].clear();
  91. for(register int i = head[u]; i; i = e[i].nxt){
  92. int v = e[i].pre;
  93. if(big[v]) continue; // if it' s a heavy son, ignore it
  94. Clear(v, u);
  95. }
  96. }
  97. inline void Calc(int u, int father) {
  98. tot[dep[u]].insert(val[u]);
  99. for(register int i = head[u]; i; i = e[i].nxt){
  100. int v = e[i].pre;
  101. if(big[v]) continue;
  102. Calc(v, u);
  103. }
  104. }
  105. int n;
  106. inline void DSU(int u, int father, int flag) { // flag = 0 : light, 1 : heavy
  107. for(register int i = head[u]; i; i = e[i].nxt){ // every light son
  108. int v = e[i].pre;
  109. if(v == son[u]) continue;
  110. DSU(v, u, 0);
  111. }
  112. if(son[u]) DSU(son[u], u, 1), big[son[u]] = true; // heavy son
  113. Calc(u, father);
  114. for(vector<Ques>::iterator it = q[u].begin(); it != q[u].end(); ++it){
  115. int t = dep[u] + it -> K;
  116. if(t <= n + 1) ans[it -> id] = tot[t].size();
  117. }
  118. if(son[u]) big[son[u]] = false; // finished getting the information from the heavy son
  119. if(!flag) Clear(u, father); // clear the information from the light son
  120. }
  121. int main() {
  122. freopen("ancestor.in", "r", stdin);
  123. freopen("ancestor.out", "w", stdout);
  124. //FileOpen();
  125. //FileSave();
  126. io >> n;
  127. R(i,1,n){
  128. scanf("%s", str + 1);
  129. val[i] = HashVal(str);
  130. int fa;
  131. io >> fa;
  132. add(fa, i);
  133. }
  134. int Q;
  135. io >> Q;
  136. R(i,1,Q){
  137. int x, K;
  138. io >> x >> K;
  139. q[x].push_back(Ques(K, i));
  140. }
  141. DFS_First(0, 0);
  142. DSU(0, 0, 0);
  143. R(i,1,Q){
  144. printf("%d\n", ans[i]);
  145. }
  146. return 0;
  147. }
  148. /*
  149. 5
  150. alice 0
  151. alice 1
  152. bob 2
  153. cindy 1
  154. bob 2
  155. 3
  156. 2 1
  157. 1 2
  158. 1 1
  159. */

例题

咕咕咕~

参考资料

总结-DSU ON TREE(树上启发式合并)的更多相关文章

  1. dsu on tree 树上启发式合并 学习笔记

    近几天跟着dreagonm大佬学习了\(dsu\ on\ tree\),来总结一下: \(dsu\ on\ tree\),也就是树上启发式合并,是用来处理一类离线的树上询问问题(比如子树内的颜色种数) ...

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

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

  3. dsu on tree[树上启发式合并学习笔记]

    dsu on tree 本质上是一个 启发式合并 复杂度 \(O(n\log n)\) 不支持修改 只能支持子树统计 不能支持链上统计- 先跑一遍树剖的dfs1 搞出来轻重儿子- 求每个节点的子树上有 ...

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

    简介 对于一颗静态树,O(nlogn)时间内处理子树的统计问题.是一种优雅的暴力. 算法思想 很显然,朴素做法下,对于每颗子树对其进行统计的时间复杂度是平方级别的.考虑对树进行一个重链剖分.虽然都基于 ...

  5. 树上启发式合并(dsu on tree)学习笔记

    有丶难,学到自闭 参考的文章: zcysky:[学习笔记]dsu on tree Arpa:[Tutorial] Sack (dsu on tree) 先康一康模板题吧:CF 600E($Lomsat ...

  6. 【Luogu U41492】树上数颜色——树上启发式合并(dsu on tree)

    (这题在洛谷主站居然搜不到--还是在百度上偶然看到的) 题目描述 给一棵根为1的树,每次询问子树颜色种类数 输入输出格式 输入格式: 第一行一个整数n,表示树的结点数 接下来n-1行,每行一条边 接下 ...

  7. 神奇的树上启发式合并 (dsu on tree)

    参考资料 https://www.cnblogs.com/zhoushuyu/p/9069164.html https://www.cnblogs.com/candy99/p/dsuontree.ht ...

  8. CF741D Arpa’s letter-marked tree and Mehrdad’s Dokhtar-kosh paths 树上启发式合并(DSU ON TREE)

    题目描述 一棵根为\(1\) 的树,每条边上有一个字符(\(a-v\)共\(22\)种). 一条简单路径被称为\(Dokhtar-kosh\)当且仅当路径上的字符经过重新排序后可以变成一个回文串. 求 ...

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

    树上启发式合并属于暴力的优化,复杂度O(nlogn) 主要解决的问题特点在于: 1.对于树上的某些信息进行查询 2.一般问题的解决不包含对树的修改,所有答案可以离线解决 算法思路:这类问题的特点在于父 ...

  10. hdu6191(树上启发式合并)

    hdu6191 题意 给你一棵带点权的树,每次查询 \(u\) 和 \(x\) ,求以 \(u\) 为根结点的子树上的结点与 \(x\) 异或后最大的结果. 分析 看到子树,直接上树上启发式合并,看到 ...

随机推荐

  1. 《C Primer Plus》第六版笔记--4~6章

    目录 第四章 字符串和格式化输入/输出 第五章 运算符.表达式和语句 第六章 C控制语句:循环 虽然匆匆忙忙,但还是要做笔记,虽然大概都知道...... 挑一些容易忘记的地方 第四章 字符串和格式化输 ...

  2. 从单例谈double-check必要性,多种单例各取所需

    theme: fancy 前言 前面铺掉了那么多都是在讲原则,讲图例.很多同学可能都觉得和设计模式不是很搭边.虽说设计模式也是理论的东西,但是设计原则可能对我们理解而言更加的抽象.不过好在原则东西不是 ...

  3. 在CabloyJS中将Webpack生成的文件自动上传到阿里云OSS

    背景 阿里云OSS提供了一个Webpack插件,可在Webpack打包结束后将webpack生成的文件自动上传到阿里云OSS中 下面看看在CabloyJS中如何使用该插件 新建项目,并配置MySQL连 ...

  4. Citus 11 for Postgres 完全开源,可从任何节点查询(Citus 官方博客)

    Citus 11.0 来了! Citus 是一个 PostgreSQL 扩展,它为 PostgreSQL 添加了分布式数据库的超能力. 使用 Citus,您可以创建跨 PostgreSQL 节点集群透 ...

  5. BUUCTF-签到题

    签到题 很简单写在介绍里面了.

  6. 你不会用node 操作mysql数据库吗?

    http://static.runoob.com/download/websites.sql这是实例 websites.sql文件1.安装node的mysql服务 npm install mysql ...

  7. 用Python实时获取Steam特惠游戏数据,我看看谁的钱包还有钱

    前言 大家好鸭, 我是小熊猫 Steam大家应该不陌生吧?不知道的话就让我们来了解一下吧~(一下简称"S") S是由美国电子游戏商Valve于2003年9月12日推出的数字发行平台 ...

  8. 自建批量更改标准BO数据程序

    by zyi

  9. Linux 批量杀死进程(详细版本)

    使用场景 当程序中有使用到多进程且进程数较多的情况,如下图,且需要通过控制台杀死所有的 GSM_run.py 的进程时,利用 kill 命令一个一个的去结束进程是及其耗时且繁琐的,这时就需要我们的ki ...

  10. 注册器机制Registry

    在众多深度学习开源库的代码中经常出现Registry代码块,例如OpenMMlab,facebookresearch和BasicSR中都使用了注册器机制.这块的代码经常会让新使用这些库的初学者感到一头 ...