线段树合并

前置知识:值域线段树+动态开点线段树。

什么是线段树合并

顾名思义,就是把两个线段树合并到一起,但是对于一般的堆式存储,显然不用合并,只有对于形态不确定的动态开点线段树才有用。

现在要合并两棵动态开点线段树Czel_X:我会启发式合并。尽管AKatuo讲过,但是太过于暴力,十分不优美。

我们不妨将合并的线段树视作一棵新的线段树,不过和这两棵线段树共用一些节点,处理方式有点类似可持久化,具体的,我们同时遍历两棵树,如果其中一个结点为空,那么我们就返回另外一个结点;否则,选一个结点作为合并之后的点,用另一个点来更新信息,然后依照题意相加或者取max等。

比如说:

向下递归时 [1,4][5,8] 区间两棵树都有,
但到了 [3,4] 时,右树没有了,所以直接返回左树的即可,对于 [7,8] 区间同理。

最后到 [1,1] 区间(这样写不太合法),由于都有,选一个当做合并后的点,然后视实现目标去更新信息即可。

例题

雨天的尾巴这道题就是线段树合并的例题。

对于每一个点,建一棵值域线段树,修改时树上差分即可,最后查询时,就把每个节点的线段树合并起来即可。

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;
}
posted @   p7gab  阅读(10)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示