[Vani有约会]雨天的尾巴 线段树合并
[Vani有约会]雨天的尾巴
线段树合并入门好题。
先别急着上线段树合并,考虑一下这题的暴力。一看就是树上差分,对于每一个节点统计每种救济粮的数量,再一遍dfs把差分的结果统计成答案。如果暴力统计,即对于每一个点开一个数组记录每种救济粮的数量,统计时再\(O(S)\)(设\(S\)为救济粮的种类数)地合并两个节点的信息,无论时间还是空间都是无法承受的,于是考虑优化。
容易想到对于每一个点开一棵值域线段树记录区间内救济粮最多的种类和相应的个数,但是这样还是会炸,所以不能用一般的线段树,考虑使用动态开点线段树。
所谓“动态开点”,其实就是不把一棵线段树建满,而只把我们需要的节点建出来。这样做就能够把空间复杂度降到\(O(mlogS)\),因为对于每一次救济粮的发放,最坏情况下只会开出4条长度为\(logS\)的链(树上差分,修改\(x\)、\(y\)、\(lca\)和\(lca\)的父亲四处)。这样做的话,线段树上每个节点的儿子就不是\(k<<1\)和\(k<<1|1\)了,需要单独记录。
其实把动态开点线段树建出来之后,线段树合并的想法就非常自然了,在这里先口胡一下,待会可以结合代码理解。对于两棵待合并的线段树上的记录相同值域的节点,考虑合并这两个节点的信息,如果某一个节点上没有信息,合并后的节点就直接继承另一个节点的信息,并且不用往下递归,否则就往下递归处理;直到统计的区间变成一个点,就合并两个点上的信息,回溯。其实和普通线段树的操作是非常相似的。事实上,这个算法的时间复杂度是\(O(mlogS)\),因为最坏情况下有\(O(m)\)条长度为\(O(logS)\)的链需要合并,所以最多只会有\(O(mlogS)\)个节点,而每个节点的信息只会被合并一次。
代码(没有刻意卡空间):
#include<cstdio>
#include<cctype>
#define R register
#define I inline
using namespace std;
const int S=100003,N=200003,M=8000003;
char buf[S],*p1,*p2;
I char gc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,S,stdin),p1==p2)?EOF:*p1++;}
I int rd(){
R int f=0; R char c=gc();
while(c<48||c>57) c=gc();
while(c>47&&c<58) f=f*10+(c^48),c=gc();
return f;
}
int h[S],s[N],g[N],d[S],t[S],p[S],q[S],r[S],v[S],f[S],c,e;
struct D{int u,c;
friend int operator>(D x,D y){return x.c^y.c?x.c>y.c:x.u<y.u;}
friend D operator+(D x,D y){return (D){x.u,x.c+y.c};}
};
struct T{int l,r; D v;}o[M];
I D max(D x,D y){return x>y?x:y;}
I void swp(int &x,int &y){x^=y,y^=x,x^=y;}
I void add(int x,int y){s[++c]=h[x],h[x]=c,g[c]=y;}
I void psu(int k){o[k].v=max(o[o[k].l].v,o[o[k].r].v);}
void ins(int &k,int l,int r,D v){if(!k) k=++e;
if(l==r){o[k].v=v+o[k].v; return ;} R int m=l+r>>1;
if(v.u<=m) ins(o[k].l,l,m,v); else ins(o[k].r,m+1,r,v); psu(k);
}
int mrg(int k,int t,int l,int r){
if(!k) return t; if(!t) return k;
if(l==r){o[k].v=o[k].v+o[t].v; return k;} R int m=l+r>>1;
o[k].l=mrg(o[k].l,o[t].l,l,m),o[k].r=mrg(o[k].r,o[t].r,m+1,r),psu(k);
return k;
}
void dfs1(int x,int f){p[x]=f,d[x]=d[f]+1,t[x]=1;
for(R int i=h[x],y,m=0;i;i=s[i])
if((y=g[i])^f){dfs1(y,x),t[x]+=t[y];
if(t[y]>m) m=t[y],q[x]=y;
}
}
void dfs2(int x,int t){r[x]=t;
if(q[x]) dfs2(q[x],t);
for(R int i=h[x],y;i;i=s[i])
if(((y=g[i])^p[x])&&(y^q[x])) dfs2(y,y);
}
int lca(int x,int y){
for(;r[x]^r[y];x=p[r[x]]) if(d[r[x]]<d[r[y]]) swp(x,y);
return d[x]<d[y]?x:y;
}
void dfs(int x){
for(R int i=h[x],y;i;i=s[i])
if((y=g[i])^p[x]) dfs(y),v[x]=mrg(v[x],v[y],1,S);
f[x]=o[v[x]].v.u;
}
int main(){
R int n=rd(),m=rd(),i,x,y,z,u;
for(i=1;i<n;++i) x=rd(),y=rd(),add(x,y),add(y,x);
for(dfs1(1,0),dfs2(1,1),i=1;i<=m;++i){
x=rd(),y=rd(),z=rd(),u=lca(x,y);
ins(v[x],1,S,(D){z,1}),ins(v[y],1,S,(D){z,1}),ins(v[u],1,S,(D){z,-1});
if(p[u]) ins(v[p[u]],1,S,(D){z,-1});
}
for(dfs(1),i=1;i<=n;++i) printf("%d\n",f[i]);
return 0;
}
我不会告诉你我因为lca写挂调了几个小时
[Vani有约会]雨天的尾巴 线段树合并的更多相关文章
- 洛谷P4556 [Vani有约会]雨天的尾巴(线段树合并)
题目背景 深绘里一直很讨厌雨天. 灼热的天气穿透了前半个夏天,后来一场大雨和随之而来的洪水,浇灭了一切. 虽然深绘里家乡的小村落对洪水有着顽固的抵抗力,但也倒了几座老房子,几棵老树被连根拔起,以及田地 ...
- 【BZOJ3307】雨天的尾巴 线段树合并
[BZOJ3307]雨天的尾巴 Description N个点,形成一个树状结构.有M次发放,每次选择两个点x,y对于x到y的路径上(含x,y)每个点发一袋Z类型的物品.完成所有发放后,每个点存放最多 ...
- BZOJ3307雨天的尾巴——线段树合并
题目描述 N个点,形成一个树状结构.有M次发放,每次选择两个点x,y对于x到y的路径上(含x,y)每个点发一袋Z类型的物品.完成所有发放后,每个点存放最多的是哪种物品. 输入 第一行数字N,M接下来N ...
- P4556 雨天的尾巴 线段树合并
使用线段树合并,每个节点维护一棵权值线段树,下标为救济粮种类,区间维护数量最多的救济粮编号(下标).所以每个节点答案即为\(tre[rot[x]]\). 然后运用树上点的差分思想,对于分发路径\(u, ...
- bzoj 3307: 雨天的尾巴 线段树合并
题目大意: N个点,形成一个树状结构.有M次发放,每次选择两个点x,y对于x到y的路径上(含x,y)每个点发一袋Z类型的物品.问完成所有发放后,每个点存放最多的是哪种物品. 题解: 首先我们为每一个节 ...
- P4556 [Vani有约会]雨天的尾巴(线段树合并+lca)
P4556 [Vani有约会]雨天的尾巴 每个操作拆成4个进行树上差分,动态开点线段树维护每个点的操作. 离线处理完向上合并就好了 luogu倍增lca被卡了5分.....于是用rmq维护.... 常 ...
- P4556 [Vani有约会]雨天的尾巴 (线段树合并)
P4556 [Vani有约会]雨天的尾巴 题意: 首先村落里的一共有n座房屋,并形成一个树状结构.然后救济粮分m次发放,每次选择两个房屋(x,y),然后对于x到y的路径上(含x和y)每座房子里发放一袋 ...
- 洛谷 P4556 [Vani有约会]雨天的尾巴 解题报告
P4556 [Vani有约会]雨天的尾巴 题目背景 深绘里一直很讨厌雨天. 灼热的天气穿透了前半个夏天,后来一场大雨和随之而来的洪水,浇灭了一切. 虽然深绘里家乡的小村落对洪水有着顽固的抵抗力,但也倒 ...
- 「Luogu4556」Vani有约会-雨天的尾巴
「Luogu4556」Vani有约会-雨天的尾巴 传送门 很显然可以考虑树上差分+桶,每次更新一条链就是把这条链上的点在桶对应位置打上 \(1\) 的标记, 最后对每个点取桶中非零值的位置作为答案即可 ...
随机推荐
- [19/04/25-星期四] GOF23_结构型模式(适配器模式、代理模式)
一.引言 结构模式:核心作用就是从程序的结构上实现松耦合,从而扩大整体的类结构,用来解决更大的问题. 二.适配器模式(adapter) 生活中假设笔记本是标准的USB接口但是外置键盘是圆形接口,这时候 ...
- 【原创】纯CSS画黄金梅丽号!
代码如下 <!DOCTYPE html> <!-- saved from url=(0055)http://jadyoap.bj.bcebos.com/ife%2FcssCatAni ...
- windows ionic bash: command not found
安装好了node.js和npm后,执行npm install -g cordova ionic后,成功安装,但是执行ionic命令后,返回 command not found. 配置好了环境变量后,仍 ...
- 利用kage把msf变成可视化远控平台
项目下载https://github.com/WayzDev/Kage/releases 这里用kali系统演示 1,先下载kage: 2,右击给予执行权限 3,启动msf msfconsole -q ...
- Unity各平台内置宏定义
属性 方法 UNITY_EDITOR #define directive for calling Unity Editor scripts from your game code. UNITY_EDI ...
- ubuntu 如何进行文件、夹删除等操作
rm [选项] 文件-f, --force 强力删除,不要求确认-i 每删除一个文件或进入一个子目录都要求确认-I 在删除超过三个文件或者递归删除前要求确认-r, -R 递归删除子目录-d, --di ...
- javascript json对象操作(基本增删改查)
/** * Json对象操作,增删改查 * * @author lellansin * @blog www.lellansin.com * @version 0.1 * * 解决一些常见的问题 * g ...
- day 93 Django学习之django自带的contentType表
Django学习之django自带的contentType表 通过django的contentType表来搞定一个表里面有多个外键的简单处理: 摘自:https://blog.csdn.net/a ...
- Delphi XE7的Splash 功能
Delphi XE5,XE6,XE7编译的程序在Android下启动会有一段时间黑屏,以前需要用Java扩展Activity增加Splash显示, 现在Delphi XE7增加了Splash Imag ...
- FreeRTOS内存管理
简介 Freertos的内存管理分别在heap_1.c,heap_2.c,heap_3.c,heap_4.c,heap_5.c个文件中,选择合适的一种应用于嵌入式项目中即可. 本文的图片中 红色部分B ...