[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\) 的标记, 最后对每个点取桶中非零值的位置作为答案即可 ...
随机推荐
- 【[LNOI2014]LCA】
这题好神啊 能够\(1A\)真是不可思议 首先看到要求的这个柿子\(\sum_{i=l}^{r}deep[LCA(i,z)]\),而且\(l\)和\(r\)并不是来自与一棵子树或者一条链,而是编号连续 ...
- Vue Spa切换页面时更改标题
在Vue组件化开发过程中,因为是单页面开发,但是有时候需要页面的title根据情况改变,于是上网查了一下,各种说法花(wo)里(kan)胡(bu)哨(dong), 于是想到一个黑科技 documet. ...
- 32、SpringBoot-整合Dubbo
分布式应用 在分布式系统中,国内常用zookeeper+dubbo组合,而Spring Boot推荐使用全栈的Spring, Spring Boot+Spring Cloud. 分布式系统: Zook ...
- [Python 网络编程] TCP编程/群聊服务端 (二)
群聊服务端 需求分析: 1. 群聊服务端需支持启动和停止(清理资源); 2. 可以接收客户端的连接; 接收客户端发来的数据 3. 可以将每条信息分发到所有客户端 1) 先搭架子: #TCP Serve ...
- No.7 - 使用 animate.css 实现一个优雅的登录框
<!DOCTYPE html> <html> <head> <meta http-equiv="content-type" content ...
- String 的字面量、常量池、构造函数和intern()函数
一.内存中的 String 对象 Java 的堆和栈 对于基本数据类型变量和对象的引用,也就是局部变量表属于栈内存: 而通过 new 关键字和 constructor 创建的对象存放在堆内存: 直接的 ...
- windows下更新npm的命令实现
Set-ExecutionPolicy Unrestricted -Scope CurrentUser -Force npm install -g npm-windows-upgrade npm-wi ...
- Java clone() 浅克隆与深度克隆
内容转自:http://www.blogjava.net/orangelizq/archive/2007/10/17/153573.html 现在Clone已经不是一个新鲜词语了,伴随着“多莉”的产生 ...
- ASP.NET MVC & WebApi 中实现Cors来让Ajax可以跨域访问 (转载)
什么是Cors? CORS是一个W3C标准,全称是"跨域资源共享"(Cross-origin resource sharing).它允许浏览器向跨源服务器,发出XMLHttpReq ...
- 二进制mysql安装相关知识
建议安装5.x版本 高版本没安装经验的慎用 1.1 关闭防火墙systemctl stop firewalld.service #停止firewall#慎用 systemctl disable fir ...