【CF600E】Lomsat gelral——树上启发式合并

(题面来自luogu)

题意翻译

一棵树有n个结点,每个结点都是一种颜色,每个颜色有一个编号,求树中每个子树的最多的颜色编号的和。

ci <= n <= 1e5

  树上启发式合并裸题。统计时先扫一遍得到出现次数最大值,然后再扫一遍看哪个颜色的出现次数与mxCnt相等。注意用一个bool数组判重,清空轻儿子贡献时要顺手把bool数组也打成false。

代码:

  1. #include <iostream>
  2. #include <cstdio>
  3. #include <cctype>
  4. #define maxn 100010
  5. using namespace std;
  6. template <typename T>
  7. void read(T &x) {
  8. x = 0;
  9. char ch = getchar();
  10. while (!isdigit(ch))
  11. ch = getchar();
  12. while (isdigit(ch))
  13. x = x * 10 + (ch ^ 48), ch = getchar();
  14. }
  15. int n;
  16. int head[maxn], top;
  17. struct E {
  18. int to, nxt;
  19. } edge[maxn << 1];
  20. inline void insert(int u, int v) {
  21. edge[++top] = (E) {v, head[u]};
  22. head[u] = top;
  23. }
  24. int son[maxn], size[maxn];
  25. long long ans[maxn];
  26. void dfs1(int u, int pre) {
  27. size[u] = 1;
  28. for (int i = head[u]; i; i = edge[i].nxt) {
  29. int v = edge[i].to;
  30. if (v == pre) continue;
  31. dfs1(v, u);
  32. size[u] += size[v];
  33. if (size[v] > size[son[u]])
  34. son[u] = v;
  35. }
  36. return;
  37. }
  38. int color[maxn], cnt[maxn], sum;
  39. int curSon, mxCnt;
  40. bool vis[maxn];
  41. void calc(int u, int pre, bool op) {
  42. op ? (++cnt[color[u]], mxCnt = max(mxCnt, cnt[color[u]])) : (--cnt[color[u]], vis[color[u]] = false);
  43. for (int i = head[u]; i; i = edge[i].nxt) {
  44. int v = edge[i].to;
  45. if (v == pre || v == curSon)
  46. continue;
  47. calc(v, u, op);
  48. }
  49. }
  50. void stats(int u, int pre) {
  51. if (cnt[color[u]] == mxCnt && !vis[color[u]])
  52. sum += color[u], vis[color[u]] = true;
  53. for (int i = head[u]; i; i = edge[i].nxt) {
  54. int v = edge[i].to;
  55. if (v != pre)
  56. stats(v, u);
  57. }
  58. return;
  59. }
  60. void dsu(int u, int pre, bool op) {
  61. for (int i = head[u]; i; i = edge[i].nxt) {
  62. int v = edge[i].to;
  63. if (v == pre || v == son[u])
  64. continue;
  65. dsu(v, u, 0);
  66. }
  67. if (son[u])
  68. dsu(son[u], u, 1), curSon = son[u];
  69. mxCnt = 0;
  70. calc(u, pre, 1);
  71. stats(u, pre);
  72. ans[u] = sum;
  73. curSon = 0;
  74. if (!op) {
  75. sum = 0;
  76. calc(u, pre, 0);
  77. }
  78. }
  79. int main() {
  80. read(n);
  81. for (int i = 1; i <= n; ++i)
  82. read(color[i]);
  83. int u, v;
  84. for (int i = 1; i < n; ++i) {
  85. read(u), read(v);
  86. insert(v, u), insert(u, v);
  87. }
  88. dfs1(1, 0);
  89. dsu(1, 0, 1);
  90. for (int i = 1; i <= n; ++i)
  91. printf("%I64d ", ans[i]);
  92. return 0;
  93. }

   交这个题的时候cf刚好炸了……

原文地址:https://www.cnblogs.com/TY02/p/11323015.html

时间: 2024-08-01 01:18:35

【CF600E】Lomsat gelral——树上启发式合并的相关文章

CF 600 E Lomsat gelral —— 树上启发式合并

题目:http://codeforces.com/contest/600/problem/E 看博客:https://blog.csdn.net/blue_kid/article/details/82192641 https://blog.csdn.net/clove_unique/article/details/60772212 还是不太明白复杂度... 代码如下: #include<iostream> #include<cstdio> #include<cstring&g

CF600E Lomsat gelral(线段树合并)

link 题目大意:给以1为根的一棵树,求树上每个点子树中出现次数最多的权值(如果有多个就求他们的和) 对每个点开一个线段树维护子树内权值的桶,dfs时候线段树合并就行了. 因为最后线段树一共插入最多 \(O(n\log n)\) 个节点,每个节点最多会被合并一次,所以复杂度是 \(O(n\log n)\) 的. #include <cstdio> #include <vector> using namespace std; struct fuck { int maxval; lo

CodeForces 600E. Lomsat gelral【树上启发式合并】

传送门 好像大家都是拿这道题作为树上启发式合并的板子题. 树上启发式合并,英文是 dsu on tree,感觉还是中文的说法更准确,因为这个算法和并查集(dsu)没有任何关系.一般用来求解有根树的所有子树的统计问题. 根据轻重儿子的各种性质,可以证明这个算法的时间复杂度为 \(O(nlogn)\),虽然看起来暴力的不行,但是却是一个很高效的算法. 算法的核心其实就是对于每个节点,先计算轻儿子,再计算重儿子,把自己和轻儿子的所有贡献累计到重儿子上去,如果自己是轻儿子,就把贡献清空. 如果掌握了树链

图论-树上启发式合并(DSU On Tree)

Disjoint Set Union On Tree ,似乎是来自 Codeforces 的一种新操作,似乎被叫做"树上启发式合并". 在不带修改的有根树子树信息统计问题中,似乎树上莫队和这个 DSU On Tree 是两类常规操作. 先对树按轻重链剖分.对于每个节点,先计算轻儿子为根的子树信息,每次计算后消除影响,再去计算其他轻儿子.然后计算重儿子为根的子树信息,不消除影响,并把轻儿子们为根的子树信息加入,再合并这个节点本身的信息.由于一个大小 \(x\) 的子树被消除影响后,都把信

【基本操作】树上启发式合并の详解

树上启发式合并是某些神仙题目的常见操作. 这里有一个讲得详细一点的,不过为了深刻记忆,我还是再给自己讲一遍吧! DSU(Disjoint Set Union),别看英文名挺高级,其实它就是并查集…… DSU on tree,也就是树上的启发式合并(众所周知,并查集最重要的优化就是启发式合并). 然后咱们来考虑一个基础题:给出一棵树,每个节点有颜色,询问一些子树中不同的颜色数量(颜色可重复).祖传数据($100000$). 当然,这道题可以被各种方法切,比如带修莫队(做法自行百度). 但莫队的时空

树上启发式合并入门

前言 树上启发式合并,即\(DSU\ on\ Tree\),是一个挺好用.挺实用的树上信息维护方法. 由于它比较简单,容易理解,因此这里也就简单记录一下吧. 前置知识:重儿子 什么是重儿子? 这应该是树链剖分中的一个概念吧.重儿子就是某个节点的子节点中,子树大小最大的节点. 适用情况 你可以很方便地给每个点染上白色和黑色,且你需要对于每个点都分别得到其子树内节点为黑.子树外节点为白的局面. 具体实现 这是一个比较贪心的过程. 考虑\(dfs\)遍历时,对于当前点的每个儿子,除最后操作的儿子以外,

“优美的暴力”——树上启发式合并

今天介绍一个神仙算法:Dsu On Tree[ 树上启发式合并 ] 这个算法用于离线处理询问子树信息,而且很好写. 但是在你没有理解它之前,这是个很鬼畜的算法. 理解后你才能真心感到它的美妙之处. 关键是它是有着媲美线段树合并的时间复杂度的“暴力”算法. 这里说一件事,我学这个东西时找了很多篇博客,它们无一例外地给出了这样一个流程: 1. 先统计一个节点所有的轻儿子 然后删除它的答案2. 再统计这个节点的重儿子 保留他的答案3. 再算一遍所有轻儿子 加到答案中上传 我当时就看的很懵逼,算一遍所有

CodeForces 375D. Tree and Queries【树上启发式合并】

传送门 题意 给出一棵 \(n\) 个结点的树,每个结点有一个颜色 \(c_i\) . 询问 \(q\) 次,每次询问以 \(v\) 结点为根的子树中,出现次数 \(\ge k\) 的颜色有多少种.树的根节点是 \(1\). 题解 反正我看见这个 \(\ge k\) 就觉得要用线段树,实际上好像不用写线段树的 Orz. 还是树上启发式合并,记录每种颜色出现的次数,然后线段树记录某种次数有多少颜色,更改就在线段树上改. 这是最后一道树上启发式合并的例题了,以后遇到再刷. #include <bit

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

这个故事告诉我们,在做一个辣鸡出题人的比赛之前,最好先看看他发明了什么新姿势= =居然直接出了道裸题 参考链接: http://codeforces.com/blog/entry/44351(原文) http://blog.csdn.net/QAQ__QAQ/article/details/53455462 这种技巧可以在O(nlogn)的时间内解决绝大多数的无修改子树询问问题. 例1 子树颜色统计 有一棵n个点的有根树,根为1,每个点有一个1~n的颜色,对于每一个点给了一个数k,要询问这个子树