(题面不是来自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. Linux中文件与目录的区别

    普通文件:存储普通数据,一般就是字符串. 目录文件:存储了一张表,该表就是该目录文件下,所有文件名和索引(inode)的映射关系. 通常父目录会存储有它目录下文件的索引映射,说白了就是一张对应关系的索 ...

  2. 实在解决不了丢失vs2019之类的msvcr110.dll之类的问题

    因为msvcr110.dll也是微软DirectX的一个组件 如果在下载VC运行库没用的情况下,可能是因为要运行的程序是win32的,但是电脑和下载的程序是64的,所以 下载一个win32的即可 如果 ...

  3. 解放双手,不写SQL!一个开源mybatis神器

    什么是通用 Mapper? 它是一个可以方便的使用 Mybatis 进行单表的增删改查优秀开源产品.它使用拦截器来实现具体的执行 Sql,完全使用原生的 Mybatis 进行操作.在 Github 上 ...

  4. PHP 获取当前页面的URL信息

    //获取当前的域名: echo $_SERVER['SERVER_NAME']; //获取来源网址,即点击来到本页的上页网址 echo $_SERVER["HTTP_REFERER" ...

  5. 《Clojure编程》笔记 第4章 多线程和并发

    目录 背景简述 第4章 多线程和并发 4.0 我的问题 4.1 术语 4.1.1 一个必须要先确定的思考基础 4.2 计算在时间和空间内的转换 4.2.1 delay 4.2.2 future 4.2 ...

  6. .NetCore中简单使用EasyNetQ

    前言 我们在.Net中使用RabbitMQ,最原始的就是基于RabbitMQ.Client进行编码,在这个过程中我们需要通过代码约定和维护队列,Exchange等.如果是自行编码封装通用型的Rabbi ...

  7. Git撤销文件修改

    在旧版本中,git的撤销工作区的文件修改是用git checkout -- <file>命令,由于容易漏了--导致和切换分支混肴,所以新版本中: - 使用git restore (--wo ...

  8. Python 列表的11个重要操作

    列表是python中内置的数据结构,它的表现形式为方括号中不同数据的集合,用逗号分隔开.列表可以用来存储相同数据类型或不同数据类型. 列表是可变的,这也是它如此常用的原因,然而在某些情况下,可变性需要 ...

  9. windows本地破解用户口令

    实验所属系列:操作系统安全 实验对象: 本科/专科信息安全专业 相关课程及专业:信息网络安全概论.计算机网络 实验时数(学分):2学时 实验类别:实践实验类 实验目的 1.了解Windows2000/ ...

  10. leetcode132:4sum

    题目描述 给出一个有n个元素的数组S,S中是否有元素a,b,c和d满足a+b+c+d=目标值?找出数组S中所有满足条件的四元组. 注意: 四元组(a.b.c.d)中的元素必须按非降序排列.(即a≤b≤ ...