#线段树合并、树上启发式合并#CF600E Lomsat gelral
题目
一棵树有\(n\)个结点,每个结点都是一种颜色,每个颜色有一个编号,求树中每个子树的最多的颜色编号的和
分析1
线段树合并,记录\(w,sum\)分别表示编号和以及颜色和,当颜色和相同时两个编号都要加,否则只加大的那一个,时间复杂度\(O(nlog_2n)\)
代码1
#include <cstdio>
#include <cctype>
#define rr register
using namespace std;
const int N=100011; long long ans[N];
struct xds{int ls,rs,sum; long long w;}h[N<<5];
struct node{int y,next;}e[N<<1];
int col[N],hs[N],root[N],cnt,k=1,n;
inline signed iut(){
rr int ans=0; rr char c=getchar();
while (!isdigit(c)) c=getchar();
while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
return ans;
}
inline void print(long long ans){
if (ans>9) print(ans/10);
putchar(ans%10+48);
}
inline void pup(int rt){
if (h[h[rt].ls].sum>h[h[rt].rs].sum)
h[rt].sum=h[h[rt].ls].sum,h[rt].w=h[h[rt].ls].w;
else h[rt].sum=h[h[rt].rs].sum,h[rt].w=h[h[rt].rs].w;
if (h[h[rt].ls].sum==h[h[rt].rs].sum) h[rt].w+=h[h[rt].ls].w;
}
inline void update(int &rt,int l,int r,int x){
if (!rt) rt=++cnt;;
if (l==r) {h[rt].w=l,++h[rt].sum; return;}
rr int mid=(l+r)>>1;
if (x<=mid) update(h[rt].ls,l,mid,x);
else update(h[rt].rs,mid+1,r,x);
pup(rt);
}
inline void merge(int nrt,int lrt,int l,int r){
if (l==r){
h[nrt].w=l,h[nrt].sum+=h[lrt].sum;
return;
}
rr int mid=(l+r)>>1;
if (h[lrt].ls){
if (!h[nrt].ls) h[nrt].ls=h[lrt].ls;
else merge(h[nrt].ls,h[lrt].ls,l,mid);
}
if (h[lrt].rs){
if (!h[nrt].rs) h[nrt].rs=h[lrt].rs;
else merge(h[nrt].rs,h[lrt].rs,mid+1,r);
}
pup(nrt);
}
inline void dfs(int x,int fa){
for (rr int i=hs[x];i;i=e[i].next)
if (e[i].y!=fa){
dfs(e[i].y,x);
merge(root[x],root[e[i].y],1,n);//合并子树
}
update(root[x],1,n,col[x]);//增加颜色
ans[x]=h[root[x]].w;
}
signed main(){
n=iut();
for (rr int i=1;i<=n;++i) col[i]=iut(),root[i]=++cnt;//每个点构一棵线段树
for (rr int i=1;i<n;++i){
rr int x=iut(),y=iut();
e[++k]=(node){y,hs[x]},hs[x]=k,
e[++k]=(node){x,hs[y]},hs[y]=k;
}
dfs(1,0);
for (rr int i=1;i<=n;++i)
print(ans[i]),putchar(i==n?10:32);
return 0;
}
分析2
树上启发式合并,自底向上处理,对于子树只处理重儿子的情况,对于轻儿子统计完就清除信息,合并到父节点时才重新算一遍,除了树上数颜色,这应该是也是一道模板题吧,因为重儿子所在的子树超过子树节点的一半,所以时间复杂度应该为\(O(nlog_2n)\),树链剖分就是用了这个性质再加上线段树、树状数组的数据结构只是再多了一个\(log_2n\)
代码2
#include <cstdio>
#include <cctype>
#define rr register
using namespace std;
const int N=100011; long long ans[N],now;
struct node{int y,next;}e[N<<1];
int col[N],hs[N],k=1,n,mx,cnt[N],root,dep[N],fat[N],son[N],big[N];
inline signed iut(){
rr int ans=0; rr char c=getchar();
while (!isdigit(c)) c=getchar();
while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
return ans;
}
inline void print(long long ans){
if (ans>9) print(ans/10);
putchar(ans%10+48);
}
inline void dfs1(int x,int fa){
dep[x]=dep[fa]+1,fat[x]=fa,son[x]=1;
for (rr int i=hs[x],mson=-1;i;i=e[i].next)
if (e[i].y!=fa){
dfs1(e[i].y,x);
son[x]+=son[e[i].y];
if (son[e[i].y]>mson) big[x]=e[i].y,mson=son[e[i].y];//处理重儿子
}
}
inline void update(int x,int z){//很好理解呀
cnt[col[x]]+=z;
if (cnt[col[x]]>mx) mx=cnt[col[x]],now=col[x];
else if (cnt[col[x]]==mx) now+=col[x];
for (rr int i=hs[x];i;i=e[i].next)
if (e[i].y!=fat[x]&&e[i].y!=root) update(e[i].y,z);
}
inline void dfs2(int x,int opt){
for (rr int i=hs[x];i;i=e[i].next)
if (e[i].y!=fat[x]&&e[i].y!=big[x]) dfs2(e[i].y,0);
if (big[x]) dfs2(big[x],1),root=big[x];
update(x,1),ans[x]=now,root=0;
if (!opt) update(x,-1),now=mx=0;
}
signed main(){
n=iut();
for (rr int i=1;i<=n;++i) col[i]=iut();
for (rr int i=1;i<n;++i){
rr int x=iut(),y=iut();
e[++k]=(node){y,hs[x]},hs[x]=k,
e[++k]=(node){x,hs[y]},hs[y]=k;
}
dfs1(1,0),dfs2(1,0);
for (rr int i=1;i<=n;++i)
print(ans[i]),putchar(i==n?10:32);
return 0;
}
#线段树合并、树上启发式合并#CF600E Lomsat gelral的更多相关文章
- P5979 [PA2014]Druzyny dp 分治 线段树 分类讨论 启发式合并
LINK:Druzyny 这题研究了一下午 终于搞懂了. \(n^2\)的dp很容易得到. 考虑优化.又有大于的限制又有小于的限制这个非常难处理. 不过可以得到在限制人数上界的情况下能转移到的最远端点 ...
- 【CodeChef EDGEST】Edges in Spanning Trees(树链剖分+树上启发式合并)
点此看题面 大致题意: 给你两棵\(n\)个点的树,对于第一棵树中的每条边\(e_1\),求存在多少条第二棵树中的边\(e_2\),使得第一棵树删掉\(e_1\)加上\(e_2\).第二棵树删掉\(e ...
- CF600E Lomsat gelral——线段树合并/dsu on tree
题目描述 一棵树有$n$个结点,每个结点都是一种颜色,每个颜色有一个编号,求树中每个子树的最多的颜色编号的和. 这个题意是真的窒息...具体意思是说,每个节点有一个颜色,你要找的是每个子树中颜色的众数 ...
- 【学习笔记/题解】树上启发式合并/CF600E Lomsat gelral
题目戳我 \(\text{Solution:}\) 树上启发式合并,是对普通暴力的一种优化. 考虑本题,最暴力的做法显然是暴力统计每一次的子树,为了避免其他子树影响,每次统计完子树都需要清空其信息. ...
- Codeforces 600E - Lomsat gelral(树上启发式合并)
600E - Lomsat gelral 题意 给出一颗以 1 为根的树,每个点有颜色,如果某个子树上某个颜色出现的次数最多,则认为它在这课子树有支配地位,一颗子树上,可能有多个有支配的地位的颜色,对 ...
- CF EDU - E. Lomsat gelral 树上启发式合并
学习:http://codeforces.com/blog/entry/44351 E. Lomsat gelral 题意: 给定一个以1为根节点的树,每个节点都有一个颜色,问每个节点的子树中,颜色最 ...
- [Codeforces600E] Lomsat gelral(树上启发式合并)
[Codeforces600E] Lomsat gelral(树上启发式合并) 题面 给出一棵N个点的树,求其所有子树内出现次数最多的颜色编号和.如果多种颜色出现次数相同,那么编号都要算进答案 N≤1 ...
- 【CF600E】Lomset gelral 题解(树上启发式合并)
题目链接 题目大意:给出一颗含有$n$个结点的树,每个节点有一个颜色.求树中每个子树最多的颜色的编号和. ------------------------- 树上启发式合并(dsu on tree). ...
- 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 ...
随机推荐
- 【LeetCode动态规划#15】最长公共子序列II
最长公共子序列(二) 描述 给定两个字符串str1和str2,输出两个字符串的最长公共子序列.如果最长公共子序列为空,则返回"-1".目前给出的数据,仅仅会存在一个最长的公共子序列 ...
- 【Java复健指南10】OOP高级01-类变量、类方法和main
类变量 什么是类变量 类变量也叫静态变量/静态属性,是该类的所有对象共享的变量,任何一个该类的对象去访问它时,取到的都是相同的值,同样任何一个该类的对象去修改它时,修改的也是同一个变量. 如何定义类变 ...
- 第137篇:重学ES6模块化
好家伙, 我原本以为学完模块化之后,就能非常顺利的完成我的项目分包, 然而并没有,这是非常重要的知识,而我没有学好 所以我决定重学一遍 本篇为<阮一峰 ECMAScript 6 (ES6 ...
- 【Azure Key Vault】客户端获取Key Vault机密信息全部失败问题分析
问题描述 在应用中获取存储在Azure Key Vault的机密信息,全部失败. 报错日志内容如下: [reactor-http-epoll-4] [reactor.netty.http.client ...
- 【Azure Developer】使用 Azure PowerShell 执行 Azure 表存储操作时遇见的4个问题
要使用PowerShell操作Azure的表存储,需要经过以下步骤: 1:必须安装 Az 和 AzTable 模块.安装命令为: #安装 Az 模块 Install-Module -Name Az - ...
- Calculate Similarity调研
Calculate Similarity - the most relevant Metrics in a Nutshell --调研学习相似度定义与计算 Zhang Zhibin 张芷彬 Many ...
- 二十: MySql 事务日志
MySql 事务日志 事务有4种特性:原子性.一致性.隔离性和持久性.那么事务的四种特性到底是基于什么机制实现呢? 事务的隔离性由 锁机制 实现. 而事务的原子性.一致性和持久性由事务的 redo 日 ...
- 选择单词后 按 ctrl + space 单词发音
需求: 在ide或其他地方,经常有单词发音不是很确定,但并不要很详细 就听个单词发音. 确定快捷键: 左手单手操作,我键盘上貌似就 左边的ctrl和空格键 还没有设定 翻译软件: 使用 pc端的 欧路 ...
- 微型MPU6050模块及串口/BLE透传的实物展示
前言 随着时间的积累,项目的沉淀.把很多做过的产品标准化掉,形成可以立即拿出来使用的产品.是非常重要的一个环节.最近,把基于MPU6050的模块微型化并使用串口透传这个产品梳理了一下.形成了标准化 ...
- 二进制文件分析工具-hexdump使用指南
一 概念: hexdump是Linux下的一个二进制文件查看工具,它可以将二进制文件转换为ASCII.八进制.十进制.十六进制 格式进行 查看. 二 用法简介: 该工具的用法十分简单,具体如下所示: ...