树链剖分+线段树维护板子

时间复杂度O(mlogn*logn)

用于解决u-v最短路径权值问题
解决u子树点权值和问题

可以将两点之间深度较深的那一个点的权值映射到这条边的权值

从而通过维护点权值间接维护边权值

每次只需要lca那一点的权值不动即可

int w[maxn];
vector<int>e[maxn];
int fa[maxn],dep[maxn],sz[maxn],son[maxn];
//每个节点的父亲,深度,以u为根的树大小,重儿子 
int top[maxn],id[maxn],nw[maxn],cnt;
//存u所在重链的顶点,u剖分后的新编号,新编号在树中所对应节点的权值
void dfs1(int u,int father){
	fa[u]=father;dep[u]=dep[father]+1;sz[u]=1;
	
	for(int v:e[u]){
		if(v==father)continue;
		dfs1(v,u);
		sz[u]+=sz[v];
		if(sz[son[u]]<sz[v])son[u]=v;
	}
}   

void dfs2(int u,int t){
	top[u]=t,id[u]=++cnt,nw[cnt]=w[u];
	if(!son[u])return;
	dfs2(son[u],t);
	
	for(int v:e[u]){
		if(v==fa[u]||v==son[u])continue;
		dfs2(v,v);//轻儿子 
	}
}

struct node{
	int l,r;
	ll sum,add;
}tr[4*maxn];

void pushup(int u){
	tr[u].sum=tr[2*u].sum+tr[2*u+1].sum;
}
void pushdown(int p){
	if(tr[p].add){
		int debt=tr[p].add;
		tr[2*p].sum+=debt*(tr[2*p].r-tr[2*p].l+1);
		tr[2*p+1].sum+=debt*(tr[2*p+1].r-tr[2*p+1].l+1);
		tr[2*p].add+=debt;
		tr[2*p+1].add+=debt;
		tr[p].add=0;
	}
}
void build(int p,int l,int r){
	tr[p]=node{l,r,nw[l],0};
	if(l==r)return;
	int m=l+r>>1;
	build(2*p,l,m);build(2*p+1,m+1,r);
	pushup(p);
}


//查询:求树从x到y节点最短路径上所有节点的值之和  
ll query(int u,int l,int r){//线段树 
	if(l<=tr[u].l&&tr[u].r<=r)return tr[u].sum;
	pushdown(u);
	int m=tr[u].l+tr[u].r>>1;
	ll res=0;
	if(l<=m)res+=query(2*u,l,r);
	if(r>m)res+=query(2*u+1,l,r);
	return res;
}
ll query_path(int u,int v){//原树 
	ll res=0;
	while(top[u]!=top[v]){
		if(dep[top[u]]<dep[top[v]])swap(u,v);
		res+=query(1,id[top[u]],id[u]);
		u=fa[top[u]];
	}
	if(dep[u]<dep[v])swap(u,v);
	res+=query(1,id[v],id[u]);
	return res;
} 



//修改:将树从x到y节点最短路径所有节点加上k

void update(int u,int l,int r,int k){//线段树 
	if(l<=tr[u].l&&r>=tr[u].r){
		tr[u].add+=k;
		tr[u].sum+=k*(tr[u].r-tr[u].l+1);
		return;
	}
	pushdown(u);
	int m=tr[u].l+tr[u].r>>1;
	if(l<=m)update(2*u,l,r,k);
	if(r>m)update(2*u+1,l,r,k);
	pushup(u);
} 

void update_path(int u,int v,int k){//原树 
	while(top[u]!=top[v]){
		if(dep[top[u]]<dep[top[v]])swap(u,v);
		update(1,id[top[u]],id[u],k);
		u=fa[top[u]];
	}
	if(dep[u]<dep[v])swap(u,v);
	update(1,id[v],id[u],k);
}




//修改/查询:树上以u为根的子树的点权和 
void update_tree(int u,int k){//线段树 
	update(1,id[u],id[u]+sz[u]-1,k);
}
ll query_tree(int u){//原树 
	return query(1,id[u],id[u]+sz[u]-1);
}



void solve(){
	int n,m,r,p;cin>>n>>m>>r>>p;
	for(int i=1;i<=n;i++){
		cin>>w[i];
	}

	for(int i=1;i<=n-1;i++){
		int u,v;cin>>u>>v;
		e[u].pb(v);
		e[v].pb(u);
	}
	dfs1(r,0);
	dfs2(r,r);
	
	build(1,1,n);
	for(int i=1;i<=m;i++){
		int opt;cin>>opt;
		int x,y,z;
		if(opt==1){
			cin>>x>>y>>z;
			update_path(x,y,z);
		}else if(opt==2){
			cin>>x>>y;
			cout<<query_path(x,y)%p<<endl;
		}else if(opt==3){
			cin>>x>>z;
			update_tree(x,z);
		}else{
			cin>>x;
			cout<<query_tree(x)%p<<endl;
		}
	}
}
posted @ 2025-03-15 10:31  Marinaco  阅读(28)  评论(0)    收藏  举报
//雪花飘落效果