线段树合并
前置知识:值域线段树+动态开点线段树。
什么是线段树合并
顾名思义,就是把两个线段树合并到一起,但是对于一般的堆式存储,显然不用合并,只有对于形态不确定的动态开点线段树才有用。
现在要合并两棵动态开点线段树Czel_X:我会启发式合并。尽管AKatuo讲过,但是太过于暴力,十分不优美。
我们不妨将合并的线段树视作一棵新的线段树,不过和这两棵线段树共用一些节点,处理方式有点类似可持久化,具体的,我们同时遍历两棵树,如果其中一个结点为空,那么我们就返回另外一个结点;否则,选一个结点作为合并之后的点,用另一个点来更新信息,然后依照题意相加或者取max等。
比如说:
向下递归时
但到了
最后到
例题
雨天的尾巴这道题就是线段树合并的例题。
对于每一个点,建一棵值域线段树,修改时树上差分即可,最后查询时,就把每个节点的线段树合并起来即可。
code:
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxn=1e5+10;
int maxr=1e5+10;
struct node {
int nxt,to;
} edge[maxn<<1];
int ans[maxn];
int head[maxn],cnt=0;
int dep[maxn],st[maxn<<1][21],rl[maxn<<1],fa[maxn],dfn[maxn<<1],num=0;
int tot=0;
struct tree {
int l,r;
int z,sum;
} t[maxn*50];
int n,m;
void pushup(int p) {
if(t[t[p].l].sum>=t[t[p].r].sum)
t[p].sum=t[t[p].l].sum,t[p].z=t[t[p].l].z;
else t[p].sum=t[t[p].r].sum,t[p].z=t[t[p].r].z;
}
void add(int u,int v) {
cnt++;
edge[cnt].to=v;
edge[cnt].nxt=head[u];
head[u]=cnt;
}
int root[maxn<<1];
void dfs(int u,int fath) {
rl[++num]=u;
dfn[u]=num,dep[u]=dep[fath]+1;
fa[u]=fath;
for(int i=head[u]; i; i=edge[i].nxt) {
int v=edge[i].to;
if(v==fath) continue;
dfs(v,u);
rl[++num]=u;
}
}
int lca(int x,int y) {
int u=dfn[x],v=dfn[y];
if(u>v)
swap(u,v);
int k=log2(v-u+1);
int uu=st[u][k],vv=st[v-(1<<k)+1][k];
if(dep[uu]<dep[vv])return uu;
else return vv;
}
void update(int &p,int x,int k,int l=1,int r=maxr) {
if(!p)p=++tot;
if(l==r) {
t[p].sum+=k;
t[p].z=x;
return ;
}
int mid=(l+r)>>1;
if(x<=mid) update(t[p].l,x,k,l,mid);
else update(t[p].r,x,k,mid+1,r);
pushup(p);
}
int merge(int u,int v,int l,int r) {
if(!u or !v) return u+v;
if(l==r) {
t[u].sum+=t[v].sum;
return u;
}
int mid=(l+r)>>1;
t[u].l=merge(t[u].l,t[v].l,l,mid);
t[u].r=merge(t[u].r,t[v].r,mid+1,r);
pushup(u);
return u;
}
void dfs2(int u,int fath) {
for(int i=head[u]; i; i=edge[i].nxt) {
int v=edge[i].to;
if(v==fath) continue;
dfs2(v,u);
root[u]=merge(root[u],root[v],1,maxr);
}
ans[u]=t[root[u]].z;
if(t[root[u]].sum==0) ans[u]=0;
}
signed main() {
cin>>n>>m;
for(int i=1; i<n; i++) {
int u,v;
cin>>u>>v;
add(u,v),add(v,u);
}
dfs(1,0);
for(int i=1; i<=num; i++)
st[i][0]=rl[i];
int sl=log2(num);
for(int j=1; j<=sl; j++) {
for(int i=1; i+(1<<j)-1<=num; i++) {
int u=st[i][j-1],v=st[i+(1<<j-1)][j-1];
if(dep[u]<dep[v])st[i][j]=u;
else st[i][j]=v;
}
}
while(m--) {
int a,b,z;
cin>>a>>b>>z;
int k=lca(a,b);
update(root[a],z,1);
update(root[b],z,1);
update(root[k],z,-1);
update(root[fa[k]],z,-1);
}
dfs2(1,0);
for(int i=1; i<=n; i++) {
cout<<ans[i]<<endl;
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!