P2590 树的统计 题目解析

一,题面

一棵树上有 n 个节点,编号分别为 1 到 n,每个节点都有一个权值 w。

我们将以下面的形式来要求你对这棵树完成一些操作:

I. CHANGE u t : 把结点 u 的权值改为 t。

II. QMAX u v: 询问从点 u 到点 v 的路径上的节点的最大权值。

III. QSUM u v: 询问从点 u 到点 v 的路径上的节点的权值和。

注意:从点 u 到点 v 的路径上的节点包括 u 和 v 本身。

对于每个 QMAX 或者 QSUM 的操作,每行输出一个整数表示要求输出的结果

二,理解

考虑线段树结合树链剖分,按照顺序梳理:

1,输入树,dfs1函数得出基本信息即 fa每个点的父节点 sz每棵子树大小 depth深度 maxson每个点的重孩子(进行重链剖分)

2,dfs2划分重链

(1)得出3个新基本信息即 top每个点所在链的链头 dfn每个点的dfs序 fn每个dfs序所对应的点的编号

(2)分链 重孩子的链头=自己链头 轻孩子单开一条链

3,对dfs序构建线段树 dfs序通用性质把树压成一维数组

同一条重链上所有节点的dfn编号是一段连续区间 可以把树上路径转成线段树区间

任意两点路径能够拆成多段连续区间 所以可以使用线段树维护树上路径

其中pushup,build,普通对于线段树查询的query和update均为模板代码

4,对于链查询的query_chain 求LCA模板代码如下 其中跳链步骤与之相同

cpp 复制代码
int LCA(int x,int y) {
    while(top[x]!=top[y]) {
        if(depth[top[x]]<depth[top[y]]) swap(x,y);
        x=fa[top[x]];
    }
    if(depth[x]<depth[y])	return x;
	else	return y;
}

借助条链,把 x 到 y 的树上路径拆成若干条完整重链 + 最后同链的一段

三,代码

详细解析在注释

cpp 复制代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=3e4+5;
int a[N],fn[N],fa[N],sz[N],depth[N],dfn[N],top[N],maxson[N],tot,ans,n,q;
struct node {
	int L,R,maxx,sum;
} st[N<<2];
vector<int > g[N];
void pushup(int root) {
	st[root].maxx=max(st[root<<1].maxx,st[root<<1|1].maxx);
	st[root].sum=st[root<<1].sum+st[root<<1|1].sum;
}
void build(int root,int L,int R) {
	st[root].L=L;
	st[root].R=R;
	if(L==R) {
		st[root].maxx=st[root].sum=a[fn[L]];//注意对dfs序打线段树要反映射回编号
		return ;
	}
	int mid=L+R>>1;
	build(root<<1,L,mid);
	build(root<<1|1,mid+1,R);
	pushup(root);
}
void update(int root,int L,int R,int k,int val) {
	if(L==R) {
		st[root].maxx=st[root].sum=val;
		return ;
	}
	int mid=L+R>>1;
	if(k<=mid)	update(root<<1,L,mid,k,val);
	else	update(root<<1|1,mid+1,R,k,val);
	pushup(root);
}
void dfs1(int u,int f) {//作用:求重儿子、sz、depth、fa 
	fa[u]=f,sz[u]=1,depth[u]=depth[f]+1;
	for(int v:g[u]) {
		if(v==f)	continue;
		dfs1(v,u);
		sz[u]+=sz[v];
		if(!maxson[u]||sz[maxson[u]]<sz[v]) { //v是否是u的重孩子
			maxson[u]=v;
		}
	}
}
void dfs2(int u,int t){//作用划分重链 t即为此时u点对应的链头
	top[u]=t; 
	dfn[u]=++tot;
	fn[tot]=u;//反映射标记dfs序对应的编号
	if(maxson[u]){//有重孩子优先走重孩子
		dfs2(maxson[u],t);//链头不变,重孩子自动成链
		for(int v:g[u]){ 
			if(v!=maxson[u]&&v!=fa[u]){//是轻孩子+不重走 
				dfs2(v,v);//轻孩子单开链,链头是自己 
			} 
		} 
	} 
}
int query(int root,int L,int R,int l,int r,int type){
	if(type==1){
		if(l<=L&&R<=r){
			return st[root].maxx;
		}
		int mid=L+R>>1;
		if(r<=mid)	return query(root<<1,L,mid,l,r,type);
		else if(l>mid)	return query(root<<1|1,mid+1,R,l,r,type);
		else	return max(query(root<<1,L,mid,l,r,type),query(root<<1|1,mid+1,R,l,r,type));
	}
	else{
		if(l<=L&&R<=r){
			return st[root].sum;
		}
		int mid=L+R>>1;
		if(r<=mid)	return query(root<<1,L,mid,l,r,type);
		else if(l>mid)	return query(root<<1|1,mid+1,R,l,r,type);
		else	return query(root<<1,L,mid,l,r,type)+query(root<<1|1,mid+1,R,l,r,type);
	}
}
int query_chain(int x,int y,int type) {
	if(type==1)	ans=-0x3f3f3f3f;
	else	ans=0;
	while(top[x]!=top[y]) {
		if(depth[top[x]]<depth[top[y]])	swap(x,y);
		int tmp=query(1,1,n,dfn[top[x]],dfn[x],type);//路径的一部分 
		if(type==1)	ans=max(ans,tmp);
		else	ans+=tmp;
		x=fa[top[x]];//跳到不同的链直到共链
	}
	if(depth[x]<depth[y])	swap(x,y);
	int tmp=query(1,1,n,dfn[y],dfn[x],type);//对dfs序打线段树,此时跳完后的x和y共链,这是最后的那部分路径
	if(type==1)	ans=max(ans,tmp);
	else	ans+=tmp;
	return ans;
}
int main() {
	scanf("%d",&n);
	for(int i=1; i<n; i++){
		int aa,bb;
		scanf("%d%d",&aa,&bb);
		g[aa].push_back(bb);
		g[bb].push_back(aa);
	}
	for(int i=1;i<=n;i++)	scanf("%d",&a[i]);
	dfs1(1,0);
	dfs2(1,1);//1的链头是自己 
	build(1,1,n);
	scanf("%d",&q);
	while(q--){
		char op[10];
		int x,y;
		scanf("%s%d%d",&op,&x,&y);
		if(op[0]=='C'){
			update(1,1,n,dfn[x],y);//对dfs序打的线段树所以找的是dfn[x] 
		}
		else{
			if(op[1]=='M'){
				printf("%d\n",query_chain(x,y,1));
			}
			else{
				printf("%d\n",query_chain(x,y,0));
			}
		}
	}
}

好久没干这活了,大肘子欢迎指正

相关推荐
imbackneverdie2 小时前
AI4S不止于分子药物:以MedPeer为代表的科研基建打开产业新增量
大数据·人工智能·算法·aigc·科研·学术·ai 4s
额鹅恶饿呃3 小时前
C语言中的数据结构和变量
c语言·数据结构·算法
运行时记录4 小时前
prompt-optimizer skill
算法
万法若空4 小时前
【数据结构-哈希表】哈希表原理
数据结构·算法·散列表
退休倒计时4 小时前
【每日一题】LeetCode 437. 路径总和 III TypeScript
算法·leetcode·typescript
学逆向的5 小时前
汇编——内存
开发语言·汇编·算法·网络安全
tachibana25 小时前
hot100 翻转二叉树(226)
java·数据结构·算法·leetcode
兰令水5 小时前
leecodecode【面试150】【2026.7.9打卡-java版本】
java·数据结构·leetcode·面试·职场和发展
生戎马6 小时前
高光谱拼接算法(七)USAC
算法