(题面不是来自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. JUC---13各种锁

    一.公平锁与非公平锁 公平锁:加锁前检查是否有排队等待的线程,优先排队等待的线程,先来先得 非公平锁:加锁时不考虑排队等待问题,直接尝试获取锁,获取不到自动到队尾等待 非公平锁性能比公平锁高5~10倍 ...

  2. 如何做好智能家居工业物联网串口转WiFi/4G/以太网无线AP方案的物联网WiFi模块选型

    2020年一场突如其来的灾难袭来,各大经济市场在不同程度被按下了"暂停键".物联网控制作为新兴行业优势凸显出来,其无接触式的交互方式,远程控制,智能控制车间,给人民带来安全.智能的 ...

  3. Reverse for ‘password_reset_complete‘ not found. ‘password_reset_complete‘ is not a valid view funct

    关注公众号"轻松学编程"了解更多 原因 在使用xadmin与django 2版本以上修改密码时会报这个错,这是由于django修改密码成功后使用的是success_url参数,而x ...

  4. 解决 ‘Could not fetch URL https://pypi.python.org’的问题

    [前提]: win10下python3和python2共存环境,但是环境变量只配置了python3 [问题]: 用pip安装一个包执行pip2 install xxx的时候报错Fatal error ...

  5. POJ1840 Eqs

    题意描述 Eqs 求一个五元方程 \(a_1x_1^3+a_2x_2^3+a_3x_3^3+a_4x_4^3+a_5x_5^3=0\) 的解的个数. 题中给出 \(a_i\) 的值并且保证 \(-50 ...

  6. WeihanLi.Npoi 1.11.0/1.12.0 Release Notes

    WeihanLi.Npoi 1.11.0/1.12.0 Release Notes Intro 最近 NPOI 扩展新更新了两个版本,感谢 shaka chow 的帮忙和支持,这两个 Feature ...

  7. print( "Hello,NumPy!" )

    print( "Hello,NumPy!" ) 学习痛苦啊,今天学,明天丢.这种天气,还是睡觉最舒服了. 咱说归说,闹归闹,但还是得学才行啊. 之前在学习的过程中一直都有记录笔记的 ...

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

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

  9. 7.1range函数和冒泡排序

    range函数和冒泡排序 一.range函数 在Python开发应用中 range函数相当重要,也比较常用: ​ 首先看range函数的原型: range(start, end, scan) ​ 参数 ...

  10. jquery DataTable插件使用自定义搜索

    $(function () { $("#pk_status").change(function () { valid = $(this).val(); if(valid){ tab ...