线段树合并的一些细节

这篇文章介绍了一个使用C++实现的算法模板,结合线段树和最近公共祖先(LCA)解决图中动态路径权值更新的问题。首先进行深度优先搜索(DFS)计算节点深度和最近公共祖先,然后构建线段树处理区间操作,最后通过DFS更新所有节点的权值。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

模板

#include<iostream>
#include<vector>
using namespace std;
const int Z=100000;
int n,m;
vector<vector<int>> a;
int deep[100005],f[20][100005],*fa=f[0],size[100005];
int dfs1(int u) {
	size[u]=1;
	deep[u]=deep[fa[u]]+1;
	for(auto&v:a[u]) {
		if(v==fa[u]) continue;
		fa[v]=u;
		size[u]+=dfs1(v);
	}
	return size[u];
}
int LCA(int x,int y) {
	if(deep[x]<deep[y]) swap(x,y);
	for(int k=19; k>=0; k--)
		if(deep[f[k][x]]>=deep[y])
			x=f[k][x];
	if(x==y) return x;
	for(int k=19; k>=0; k--)
		if(f[k][x]^f[k][y])
			x=f[k][x],
			y=f[k][y];
	return fa[x];
}
struct node {
	int l,r;
	pair<int,int> v;//最大值权值,最大值的最小编号的相反数
	int lc,rc;
} t[100010*100]; //注意空间
int cnt;
int newn(int u,bool flag) {//newnode
	int mid=t[u].l+t[u].r>>1;
	if(!flag)//左儿子
		t[t[u].lc=++cnt]= {t[u].l,mid,{-1e9,-t[u].l},0,0}; //最大值编号先为左端点
	else
		t[t[u].rc=++cnt]= {mid+1,t[u].r,{-1e9,-mid-1},0,0};
	return cnt;
}
void push_up(int u) {
	t[u].v={-1e9,-1e9};
	if(t[u].lc) t[u].v=max(t[u].v,t[t[u].lc].v);
	if(t[u].rc) t[u].v=max(t[u].v,t[t[u].rc].v);
}
void push(int u,int p,int v) {
	if(t[u].l==t[u].r&&t[u].l==p)
		if(t[u].v.first==-1e9)
			t[u].v.first=v;
		else
			t[u].v.first+=v;
	else {
		int mid=t[u].l+t[u].r>>1;
		if(p<=mid)
			if(t[u].lc) push(t[u].lc,p,v);
			else push(newn(u,0),p,v);
		else if(t[u].rc) push(t[u].rc,p,v);
		else push(newn(u,1),p,v);
		push_up(u);
	}
	if(!t[u].v.first)
		t[u].v.first=-1e9;
}
pair<int,int> find(int u,int l,int r) {
	if(l<=t[u].l&&t[u].r<=r)
		return t[u].v;
	int mid=t[u].l+t[u].r>>1;
	pair<int,int> ans= {-1e9,-1e9};
	if(l<=mid&&t[u].lc)
		ans=max(ans,find(t[u].lc,l,r));
	if(mid<r&&t[u].rc)
		ans=max(ans,find(t[u].rc,l,r));
	return ans;
}
void merge(int u1,int u2) {
	//u1<-u2
	if(t[u1].lc&&t[u1].rc) {
		if(t[u2].lc) merge(t[u1].lc,t[u2].lc);
		if(t[u2].rc) merge(t[u1].rc,t[u2].rc);
	} else {
		if(t[u1].lc&&t[u2].lc) merge(t[u1].lc,t[u2].lc);
		if(!t[u1].lc) t[u1].lc=t[u2].lc;
		if(t[u1].rc&&t[u2].rc) merge(t[u1].rc,t[u2].rc);
		if(!t[u1].rc) t[u1].rc=t[u2].rc;
		if(!t[u1].lc&&!t[u1].rc) {
			if(t[u2].v.first^(int)-1e9) {
				if(t[u1].v.first==-1e9)
					t[u1].v.first=t[u2].v.first;
				else
					t[u1].v.first+=t[u2].v.first;
				if(!t[u1].v.first)
					t[u1].v.first=-1e9;
			}
		}
	}
	if(t[u1].lc||t[u1].rc)push_up(u1);
}
int ans[100005];
void dfs2(int u) {
	for(auto&v:a[u])
		if(v^fa[u])
			dfs2(v),merge(u,v);
	pair<int,int> ansx=find(u,1,Z);
	ans[u]=ansx.first==-1e9?0:-ansx.second;
}
int main() {
	cin>>n>>m;
	a.resize(n+1);
	for(int i=1; i<n; i++) {
		int u,v;
		cin>>u>>v;
		a[u].push_back(v);
		a[v].push_back(u);
	}
	dfs1(1);
//  for(int i=1;i<=n;i++)
//      cout<<fa[i]<<' ';
//  cout<<endl;
	for(int k=1; k<20; k++)
		for(int i=1; i<=n; i++)
			f[k][i]=f[k-1][f[k-1][i]];
	for(int i=1; i<=n; i++) //前n个节点必然是根节点
		t[i]= {1,Z,{-1e9,0},0,0};
	cnt=n;
	while(m--) {
		int x,y,z;
		cin>>x>>y>>z;
		int lca=LCA(x,y);
		push(x,z,1);
		push(y,z,1);
		push(lca,z,-1);
		if(fa[lca]) push(fa[lca],z,-1);
	}
	dfs2(1);
	for(int i=1; i<=n; i++)
		cout<<ans[i]<<endl;
	return 0;
}
  1. 注意当线段树节点权值为0的时候置为负无穷
  2. 注意merge中,必须有孩子才能push_up

后记

于是皆大欢喜。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值