(题面不是来自Luogu)

题目描述

有一个大小为n且以1为根的树,树上每个点都有对应的颜色ci。现给出m次询问v, k,问以v为根的子树中有多少种颜色至少出现了k次。

输入格式

第一行两个数n,m表示树的大小以及询问的次数。

第二行n个数表示树上每个结点的颜色。

接下来的n-1行,每行两个数a, b表示树上的边。

接下来m行,每行两个数v, k表示询问。

输出格式

m行,每行一个数表示第i次询问的答案。

样例输入1

8 5
1 2 2 3 3 2 3 3
1 2
1 5
2 3
2 4
5 6
5 7
5 8
1 2
1 3
1 4
2 3
5 3

样例输出1

2
2
1
0
1

样例输入2

4 1
1 2 3 4
1 2
2 3
3 4
1 1

样例输出2

4

数据范围

2≤n≤100000

1≤m≤100000

1≤ci≤100000

1≤a, b≤n, a≠b

1≤v≤n, 1≤k≤100000

对于其中30%的数据保证n,m≤100且ci≤n

对于其中60%的数据保证n≤5000

  (7:20)今天为了打这个题的正解学了dsu on tree。需要学习的同学请看我的上一篇博客。

  树上启发式合并主要解决的就是这类静态子树统计问题。

  对于这题,我们维护两个数组cnt和upr,分别表示某种颜色出现的数量和出现次数大于某个次数的颜色的种类数。每次暴力累计子树信息时,出现一个颜色先++cnt[color[i]],然后++upr[cnt[color[i]]]。容易想到,这样可以保证不重不漏地统计到所有达到upr[i]的颜色,并且不会重复累加。剩下的套dsu on tree板子即可。

代码:

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

【CF375D】Trees and Queries——树上启发式合并的更多相关文章

  1. 【CodeChef EDGEST】Edges in Spanning Trees(树链剖分+树上启发式合并)

    点此看题面 大致题意: 给你两棵\(n\)个点的树,对于第一棵树中的每条边\(e_1\),求存在多少条第二棵树中的边\(e_2\),使得第一棵树删掉\(e_1\)加上\(e_2\).第二棵树删掉\(e ...

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

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

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

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

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

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

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

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

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

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

  7. Codeforces 208E - Blood Cousins(树上启发式合并)

    208E - Blood Cousins 题意 给出一棵家谱树,定义从 u 点向上走 k 步到达的节点为 u 的 k-ancestor.多次查询,给出 u k,问有多少个与 u 具有相同 k-ance ...

  8. Codeforces 600E - Lomsat gelral(树上启发式合并)

    600E - Lomsat gelral 题意 给出一颗以 1 为根的树,每个点有颜色,如果某个子树上某个颜色出现的次数最多,则认为它在这课子树有支配地位,一颗子树上,可能有多个有支配的地位的颜色,对 ...

  9. csu1811(树上启发式合并)

    csu1811 题意 给定一棵树,每个节点有颜色,每次仅删掉第 \(i\) 条边 \((a_i, b_i)\) ,得到两颗树,问两颗树节点的颜色集合的交集. 分析 转化一下,即所求答案为每次删掉 \( ...

随机推荐

  1. ansible-hoc命令行

    ansible一种开源的自动化工具 ansible: hoc命令行: 是一款开源的自动化运维工具 python paramiko #模拟ssh协议批量管理主机 jinja2 #模板语言,主要用来传递变 ...

  2. 隐马尔科夫模型 HMM(Hidden Markov Model)

    本科阶段学了三四遍的HMM,机器学习课,自然语言处理课,中文信息处理课:如今学研究生的自然语言处理,又碰见了这个老熟人: 虽多次碰到,但总觉得一知半解,对其了解不够全面,借着这次的机会,我想要直接搞定 ...

  3. python的多线程和java的多线程之间的区别

    在python中,由于Cpython解释器的全局解释器的存在,那么多线程的话在同一时刻只能有一个线程执行,意思就是python中的多线程只能并发执行, 没有办法实现真正的并行,也就是无法利用多核CPU ...

  4. python机器学习实现线性回归

    线性回归 关注公众号"轻松学编程"了解更多. [关键词]最小二乘法,线性 一.普通线性回归 1.原理 分类的目标变量是标称型数据,而回归将会对连续型的数据做出预测. 应当怎样从一大 ...

  5. 我的第三次C语言作业

    我的第三次C语言作业 这个作业属于哪个课程 https://edu.cnblogs.com/campus/zswxy/SE2020-2 这个作业要求在哪里 https://edu.cnblogs.co ...

  6. 用GitHub Pages搭建博客(七)

    本篇介绍百度统计.百度搜索 一般来讲,部署了一个网站后,我们需要知道网站的浏览量,以便知道网站是否有人访问. 在Jekyll的模板中,由于国外开发者更多,一般的主题默认都开发了谷歌统计(Google ...

  7. [阿里DIN] 从模型源码梳理TensorFlow的乘法相关概念

    [阿里DIN] 从模型源码梳理TensorFlow的乘法相关概念 目录 [阿里DIN] 从模型源码梳理TensorFlow的乘法相关概念 0x00 摘要 0x01 矩阵乘积 1.1 matmul pr ...

  8. HTML5+CSS3热门活动页面

    <!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8" ...

  9. Pycharm激活码无偿分享,2020年最新Pycharm永久激活码!

    2020年10月7日08:04:34更新的Pycharm激活码,还热乎着呢,速用~ 如果下边的这个Pycharm激活码过期失效了的话,大家可以关注我的微信公众号:Python联盟,然后回复" ...

  10. python super继承用法

    子类对父类的继承一般写法为1, 高级方法为super. 1 # 1,普通继承 2 #新建一个父类 3 class Father(): 4 def father(self,message): 5 pri ...