(题面不是来自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. 在CentOS 8 上 部署 .Net Core 应用程序

    在Centos 8 上 部署 .Net Core 应用程序     -- 记录篇 1.更新dnf 源 1 dnf update 2.安装 Asp.Net Core 运行时 1 dnf install ...

  2. pytorch 图像分类数据集(Fashion-MNIST)

    import torch import torchvision import torchvision.transforms as transforms import matplotlib.pyplot ...

  3. [Luogu P1119] 灾后重建 (floyd)

    题面 传送门:https://www.luogu.org/problemnew/show/P1119 Solution 这题的思想很巧妙. 首先,我们可以考虑一下最暴力的做法,对每个时刻的所有点都求一 ...

  4. 力扣 - 19. 删除链表的倒数第N个节点

    目录 题目 思路1 代码实现 思路2 代码实现 题目 给定一个链表,删除链表的倒数第 n 个节点,并且返回链表的头结点. 示例: 给定一个链表: 1->2->3->4->5, ...

  5. SpringMVC 直接返回中文字符串时,出现乱码(?)的问题

    在springmvc.xml中加入下面配置得以解决. <mvc:annotation-driven> <mvc:message-converters> <bean cla ...

  6. H5--自动刷新

    每30秒中刷新当前html页面: <meta http-equiv="refresh" content="30">

  7. 1、Web应用

    一 Web应用的组成 接下来我们学习的目的是为了开发一个Web应用程序,而Web应用程序是基于B/S架构的,其中B指的是浏览器,负责向S端发送请求信息,而S端会根据接收到的请求信息返回相应的数据给浏览 ...

  8. C++动态存储方式与静态存储方式

    如果从变量值存在的时间(即生存期)来分,可将程序中的变量分为:动态存储方式和静态存储方式.它们所占用的存储空间区域不同. C++存储空间区域 代码区:存放可执行程序的程序代码.静态存储区:存放静态变量 ...

  9. Java 中数组转换为 List

    目录 1 - int 型数组转换为 List 2 - List 转换为 int 型数组 3 - String 型数组转换为 List 4 - List 转换为 String 型数组 版权声明 开发中经 ...

  10. socket connect tcp_v4_connect

    tcp_v4_connect /* This will initiate an outgoing connection. tcp_v4_connect函数初始化一个对外的连接请求,创建一个SYN包并发 ...