#线段树合并、树上启发式合并#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 ...
随机推荐
- xHook 源码解析
xHook 是爱奇艺开源的一个PLT Hook 框架 项目地址: https://github.com/iqiyi/xHook 该项目实现了 PTL/GOT Hook PTL hook 的本质是修改内 ...
- auth模块的一些方法
auth模块 auth模块是cookie和session的升级版,auth模块是对登录认证方法的一种封装,之前我们获取用户输入的用户名及密码后需要自己从user表里查询有没有用户名和密码符合的对象,而 ...
- SSH不对称密钥自动登入服务器
SSH不对称密钥自动登入服务器 1.先在自己的电脑上创建密钥对 ssh-keygen -t rsa Windows下生成SSH密钥 $ ssh-keygen -t rsa -C "youre ...
- 【Azure Redis 缓存】Redis的监控方式? 是否有API接口调用来获取监控值
问题描述 对于PaaS的Azure Cache for Redis,Azure中有哪些监控方式?是否能有api接口调用来获取监控值? 问题答案 1) 在Redis的门户中,使用Metrics查看Red ...
- 机器学习可解释性--shapvalue
A Unified Approach to Interpreting Model Predictions trusting a prediction or trusting a model 如果⼀个机 ...
- POM模式核心思想?
对页面元素进行封装为类的属性 对用例执行流程设计成类的实例方法 通过定义好的页面类实例化一个对象,通过对象调用实例方法执行用例 核心作用: 可以较少代码的冗余,方便后面维护,如果页面元素发生改变, 只 ...
- Java 一悟结束异常处理 Biu丶
- C++ //常用算术生成算法 //#include<numeric> accumulate //fill //向容器中填充指定的元素
1 //常用算术生成算法 //#include<numeric> accumulate 2 //fill //向容器中填充指定的元素 3 #include<iostream> ...
- Vite-vue3 架构设计
Vite-vue3 架构设计 基础信息 Gitee项目地址:https://gitee.com/pengchenggang/vite-vue3 1 创建vite-vue3 初始化脚本 $ npm in ...
- ble的notification和indication的区别和联系
Ble服务端传输消息有两个常用手段,notification和indication.那么这两者之间有什么区别呢? Notification 不需要应答,所以服务端发送的消息,它自己并不知道消息是否发送 ...