【CF600E】Lomset gelral 题解(树上启发式合并)
题目大意:给出一颗含有$n$个结点的树,每个节点有一个颜色。求树中每个子树最多的颜色的编号和。
-------------------------
树上启发式合并(dsu on tree)。
我们先考虑暴力怎么做。遍历整颗树,暴力枚举子树然后用桶维护颜色个数。这样做是$O(n^2)$的,显然会T。我们需要一种更快的算法:树上启发式合并。
关于启发式算法的介绍,详见OI Wiki。本文只介绍树上启发式合并算法。本题的解法:
每处理完一颗子树,我们都要把桶清空一次,以免对它的兄弟造成影响。而这样做还要从它的祖先遍历一遍,浪费时间。
我们发现:遍历最后一颗子树时,桶是不用清空的。因为遍历完那颗子树后可以直接把答案加入$ans$中。那我们肯定选重儿子啊,省时省力。遍历轻儿子相对不费事。
看起来是不是没有快多少?实际上它是$O(n\log n)$的。下面是证明:
对于每个节点,它被计算的次数就是它到根节点路径的轻边个数。
而结点往上跳一次,子树大小至少为原来两倍,所以轻边个数最多是$\log n$。所以时间复杂度$O(n\log n)$。
证明过程跟树链剖分的有点像。
代码:
- #include<bits/stdc++.h>
- #define int long long
- using namespace std;
- int n,color[],bucket[],ans[];
- int size[],son[],sum,mx;
- int head[],cnt;
- struct node
- {
- int next,to;
- }edge[];
- inline int read()
- {
- int x=,f=;char ch=getchar();
- while(!isdigit(ch)){if (ch=='-') f=-;ch=getchar();}
- while(isdigit(ch)){x=x*+ch-'';ch=getchar();}
- return x*f;
- }
- inline void add(int from,int to)
- {
- edge[++cnt].next=head[from];
- edge[cnt].to=to;
- head[from]=cnt;
- }
- inline void dfs_son(int now,int fa)
- {
- size[now]=;
- int mx=,p=;
- for (int i=head[now];i;i=edge[i].next)
- {
- int to=edge[i].to;
- if (to==fa) continue;
- dfs_son(to,now);
- size[now]+=size[to];
- if (size[to]>mx)
- {
- mx=size[to];
- p=to;
- }
- }
- if (p) son[p]=;
- }
- void getans(int x,int f,int p){
- bucket[color[x]]++;
- if(bucket[color[x]]>mx){
- mx=bucket[color[x]];
- sum=color[x];
- }else if(bucket[color[x]]==mx)sum+=color[x];
- for(int i=head[x];i;i=edge[i].next){
- int y=edge[i].to;
- if(y==f || y==p)continue;
- getans(y,x,p);
- }
- }
- inline void init(int now,int fa)
- {
- bucket[color[now]]--;
- for (int i=head[now];i;i=edge[i].next)
- {
- int to=edge[i].to;
- if (to==fa) continue;
- init(to,now);
- }
- }
- inline void dfs(int now,int fa)
- {
- int p=;
- for (int i=head[now];i;i=edge[i].next)
- {
- int to=edge[i].to;
- if (to==fa) continue;
- if (!son[to])
- {
- dfs(to,now);
- init(to,now);
- sum=mx=;
- }
- else p=to;
- }
- if (p) dfs(p,now);
- getans(now,fa,p);
- ans[now]=sum;
- }
- signed main()
- {
- n=read();
- for (int i=;i<=n;i++) color[i]=read();
- for (int i=;i<n;i++)
- {
- int x=read(),y=read();
- add(x,y);add(y,x);
- }
- dfs_son(,);
- dfs(,);
- for (int i=;i<=n;i++) printf("%lld ",ans[i]);
- return ;
- }
【CF600E】Lomset gelral 题解(树上启发式合并)的更多相关文章
- [Codeforces600E] Lomsat gelral(树上启发式合并)
[Codeforces600E] Lomsat gelral(树上启发式合并) 题面 给出一棵N个点的树,求其所有子树内出现次数最多的颜色编号和.如果多种颜色出现次数相同,那么编号都要算进答案 N≤1 ...
- Codeforces 600E - Lomsat gelral(树上启发式合并)
600E - Lomsat gelral 题意 给出一颗以 1 为根的树,每个点有颜色,如果某个子树上某个颜色出现的次数最多,则认为它在这课子树有支配地位,一颗子树上,可能有多个有支配的地位的颜色,对 ...
- Codeforces 600E Lomsat gelral (树上启发式合并)
题目链接 Lomsat gelral 占坑……等深入理解了再来补题解…… #include <bits/stdc++.h> using namespace std; #define rep ...
- 【学习笔记/题解】树上启发式合并/CF600E Lomsat gelral
题目戳我 \(\text{Solution:}\) 树上启发式合并,是对普通暴力的一种优化. 考虑本题,最暴力的做法显然是暴力统计每一次的子树,为了避免其他子树影响,每次统计完子树都需要清空其信息. ...
- CF EDU - E. Lomsat gelral 树上启发式合并
学习:http://codeforces.com/blog/entry/44351 E. Lomsat gelral 题意: 给定一个以1为根节点的树,每个节点都有一个颜色,问每个节点的子树中,颜色最 ...
- dsu on tree 树上启发式合并 学习笔记
近几天跟着dreagonm大佬学习了\(dsu\ on\ tree\),来总结一下: \(dsu\ on\ tree\),也就是树上启发式合并,是用来处理一类离线的树上询问问题(比如子树内的颜色种数) ...
- 树上启发式合并(dsu on tree)学习笔记
有丶难,学到自闭 参考的文章: zcysky:[学习笔记]dsu on tree Arpa:[Tutorial] Sack (dsu on tree) 先康一康模板题吧:CF 600E($Lomsat ...
- 神奇的树上启发式合并 (dsu on tree)
参考资料 https://www.cnblogs.com/zhoushuyu/p/9069164.html https://www.cnblogs.com/candy99/p/dsuontree.ht ...
- dsu on tree (树上启发式合并) 详解
一直都没出过算法详解,昨天心血来潮想写一篇,于是 dsu on tree 它来了 1.前置技能 1.链式前向星(vector 建图) 2.dfs 建树 3.剖分轻重链,轻重儿子 重儿子 一个结点的所有 ...
随机推荐
- 原生JS实现树状结构列表
树状结构列表,这个技术点之前有写过了,是基于vue讲解,但似乎都没有解决痛点,最基础的原生JS该怎么实现呢? 这篇文章会全面详细的介绍树状结构列表的实现,从数据处理成树状结构,到动态生成dom节点渲染 ...
- Spring IoC 公共注解详解
前言 本系列全部基于 Spring 5.2.2.BUILD-SNAPSHOT 版本.因为 Spring 整个体系太过于庞大,所以只会进行关键部分的源码解析. 什么是公共注解?公共注解就是常见的Java ...
- Jenkins - 解决集成 jmeter+ant 发送邮件时报错:java.lang.ClassNotFoundException: javax.mail.internet.MimeMessage
jenkins + jmeter +ant 发送邮件失败 问题原因 其实就是缺失 jar 包,导致某些类找不到了 解决方案 点击该网站,下载commons-email.jar包 点击该网站,下载act ...
- Python 图像处理 OpenCV (13): Scharr 算子和 LOG 算子边缘检测技术
前文传送门: 「Python 图像处理 OpenCV (1):入门」 「Python 图像处理 OpenCV (2):像素处理与 Numpy 操作以及 Matplotlib 显示图像」 「Python ...
- 数据可视化之DAX篇(十七)Power BI表格总计行错误的终极解决方案
https://zhuanlan.zhihu.com/p/68183990 我在知识星球收到的问题中,关于表格和矩阵(以下统称表格)总计行错误算是常见的问题之一了,不少初学者甚为不解,在Excel透视 ...
- Linux基础入门(一)初识Shell
Linux基础入门(一)初识Shell shell是什么 Shell 是一个用 C 语言编写的程序,它是用户使用 Linux 的桥梁.Shell 既是一种命令语言,又是一种程序设计语言. Shell ...
- equals方法与==关系的总结
/** * ==&&equals区别 * * ==比较的是栈内存中的值 * 对于基本类型数据,比较的是栈内存中的值 * 对于引用数据类型,比较的是栈内存中的值(值的真是含义是一个地址) ...
- 从连接器组件看Tomcat的线程模型——BIO模式
在高版本的Tomcat中,默认的模式都是使用NIO模式,在Tomcat 9中,BIO模式的实现Http11Protocol甚至都已经被删除了.但是了解BIO的工作机制以及其优缺点对学习其他模式有有帮助 ...
- Ethical Hacking - NETWORK PENETRATION TESTING(20)
MITM - Capturing Screen Of Target & Injecting a Keylogger ScreenShotter Plugin: ScreenShotter: U ...
- Java中hashCode方法的理解以及此小结的总结练习(代码)
笔记: “散列码”就是用来把一堆对象散到各自的队列里去的一种标识码. 举个形象一点的例子,一年有 365 天,从 1 编号到 365,下面我定义一种编码方法,每个人按照他生日那天的编号作为他的标识码, ...