题目链接

题目大意:给出一颗含有$n$个结点的树,每个节点有一个颜色。求树中每个子树最多的颜色的编号和。

-------------------------

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

我们先考虑暴力怎么做。遍历整颗树,暴力枚举子树然后用桶维护颜色个数。这样做是$O(n^2)$的,显然会T。我们需要一种更快的算法:树上启发式合并。

关于启发式算法的介绍,详见OI Wiki。本文只介绍树上启发式合并算法。本题的解法:

每处理完一颗子树,我们都要把桶清空一次,以免对它的兄弟造成影响。而这样做还要从它的祖先遍历一遍,浪费时间。

我们发现:遍历最后一颗子树时,桶是不用清空的。因为遍历完那颗子树后可以直接把答案加入$ans$中。那我们肯定选重儿子啊,省时省力。遍历轻儿子相对不费事。

看起来是不是没有快多少?实际上它是$O(n\log n)$的。下面是证明:

对于每个节点,它被计算的次数就是它到根节点路径的轻边个数。

而结点往上跳一次,子树大小至少为原来两倍,所以轻边个数最多是$\log n$。所以时间复杂度$O(n\log n)$。

证明过程跟树链剖分的有点像。

代码:

  1. #include<bits/stdc++.h>
  2. #define int long long
  3. using namespace std;
  4. int n,color[],bucket[],ans[];
  5. int size[],son[],sum,mx;
  6. int head[],cnt;
  7. struct node
  8. {
  9. int next,to;
  10. }edge[];
  11. inline int read()
  12. {
  13. int x=,f=;char ch=getchar();
  14. while(!isdigit(ch)){if (ch=='-') f=-;ch=getchar();}
  15. while(isdigit(ch)){x=x*+ch-'';ch=getchar();}
  16. return x*f;
  17. }
  18. inline void add(int from,int to)
  19. {
  20. edge[++cnt].next=head[from];
  21. edge[cnt].to=to;
  22. head[from]=cnt;
  23. }
  24. inline void dfs_son(int now,int fa)
  25. {
  26. size[now]=;
  27. int mx=,p=;
  28. for (int i=head[now];i;i=edge[i].next)
  29. {
  30. int to=edge[i].to;
  31. if (to==fa) continue;
  32. dfs_son(to,now);
  33. size[now]+=size[to];
  34. if (size[to]>mx)
  35. {
  36. mx=size[to];
  37. p=to;
  38. }
  39. }
  40. if (p) son[p]=;
  41. }
  42. void getans(int x,int f,int p){
  43. bucket[color[x]]++;
  44. if(bucket[color[x]]>mx){
  45. mx=bucket[color[x]];
  46. sum=color[x];
  47. }else if(bucket[color[x]]==mx)sum+=color[x];
  48. for(int i=head[x];i;i=edge[i].next){
  49. int y=edge[i].to;
  50. if(y==f || y==p)continue;
  51. getans(y,x,p);
  52. }
  53. }
  54. inline void init(int now,int fa)
  55. {
  56. bucket[color[now]]--;
  57. for (int i=head[now];i;i=edge[i].next)
  58. {
  59. int to=edge[i].to;
  60. if (to==fa) continue;
  61. init(to,now);
  62. }
  63. }
  64. inline void dfs(int now,int fa)
  65. {
  66. int p=;
  67. for (int i=head[now];i;i=edge[i].next)
  68. {
  69. int to=edge[i].to;
  70. if (to==fa) continue;
  71. if (!son[to])
  72. {
  73. dfs(to,now);
  74. init(to,now);
  75. sum=mx=;
  76. }
  77. else p=to;
  78. }
  79. if (p) dfs(p,now);
  80. getans(now,fa,p);
  81. ans[now]=sum;
  82. }
  83. signed main()
  84. {
  85. n=read();
  86. for (int i=;i<=n;i++) color[i]=read();
  87. for (int i=;i<n;i++)
  88. {
  89. int x=read(),y=read();
  90. add(x,y);add(y,x);
  91. }
  92. dfs_son(,);
  93. dfs(,);
  94. for (int i=;i<=n;i++) printf("%lld ",ans[i]);
  95. return ;
  96. }

【CF600E】Lomset gelral 题解(树上启发式合并)的更多相关文章

  1. [Codeforces600E] Lomsat gelral(树上启发式合并)

    [Codeforces600E] Lomsat gelral(树上启发式合并) 题面 给出一棵N个点的树,求其所有子树内出现次数最多的颜色编号和.如果多种颜色出现次数相同,那么编号都要算进答案 N≤1 ...

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

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

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

    题目链接 Lomsat gelral 占坑……等深入理解了再来补题解…… #include <bits/stdc++.h> using namespace std; #define rep ...

  4. 【学习笔记/题解】树上启发式合并/CF600E Lomsat gelral

    题目戳我 \(\text{Solution:}\) 树上启发式合并,是对普通暴力的一种优化. 考虑本题,最暴力的做法显然是暴力统计每一次的子树,为了避免其他子树影响,每次统计完子树都需要清空其信息. ...

  5. CF EDU - E. Lomsat gelral 树上启发式合并

    学习:http://codeforces.com/blog/entry/44351 E. Lomsat gelral 题意: 给定一个以1为根节点的树,每个节点都有一个颜色,问每个节点的子树中,颜色最 ...

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

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

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

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

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

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

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

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

随机推荐

  1. 原生JS实现树状结构列表

    树状结构列表,这个技术点之前有写过了,是基于vue讲解,但似乎都没有解决痛点,最基础的原生JS该怎么实现呢? 这篇文章会全面详细的介绍树状结构列表的实现,从数据处理成树状结构,到动态生成dom节点渲染 ...

  2. Spring IoC 公共注解详解

    前言 本系列全部基于 Spring 5.2.2.BUILD-SNAPSHOT 版本.因为 Spring 整个体系太过于庞大,所以只会进行关键部分的源码解析. 什么是公共注解?公共注解就是常见的Java ...

  3. Jenkins - 解决集成 jmeter+ant 发送邮件时报错:java.lang.ClassNotFoundException: javax.mail.internet.MimeMessage

    jenkins + jmeter +ant 发送邮件失败 问题原因 其实就是缺失 jar 包,导致某些类找不到了 解决方案 点击该网站,下载commons-email.jar包 点击该网站,下载act ...

  4. Python 图像处理 OpenCV (13): Scharr 算子和 LOG 算子边缘检测技术

    前文传送门: 「Python 图像处理 OpenCV (1):入门」 「Python 图像处理 OpenCV (2):像素处理与 Numpy 操作以及 Matplotlib 显示图像」 「Python ...

  5. 数据可视化之DAX篇(十七)Power BI表格总计行错误的终极解决方案

    https://zhuanlan.zhihu.com/p/68183990 我在知识星球收到的问题中,关于表格和矩阵(以下统称表格)总计行错误算是常见的问题之一了,不少初学者甚为不解,在Excel透视 ...

  6. Linux基础入门(一)初识Shell

    Linux基础入门(一)初识Shell shell是什么 Shell 是一个用 C 语言编写的程序,它是用户使用 Linux 的桥梁.Shell 既是一种命令语言,又是一种程序设计语言. Shell ...

  7. equals方法与==关系的总结

    /** * ==&&equals区别 * * ==比较的是栈内存中的值 * 对于基本类型数据,比较的是栈内存中的值 * 对于引用数据类型,比较的是栈内存中的值(值的真是含义是一个地址) ...

  8. 从连接器组件看Tomcat的线程模型——BIO模式

    在高版本的Tomcat中,默认的模式都是使用NIO模式,在Tomcat 9中,BIO模式的实现Http11Protocol甚至都已经被删除了.但是了解BIO的工作机制以及其优缺点对学习其他模式有有帮助 ...

  9. Ethical Hacking - NETWORK PENETRATION TESTING(20)

    MITM - Capturing Screen Of Target & Injecting a Keylogger ScreenShotter Plugin: ScreenShotter: U ...

  10. Java中hashCode方法的理解以及此小结的总结练习(代码)

    笔记: “散列码”就是用来把一堆对象散到各自的队列里去的一种标识码. 举个形象一点的例子,一年有 365 天,从 1 编号到 365,下面我定义一种编码方法,每个人按照他生日那天的编号作为他的标识码, ...