PS:如果读过题了可以跳过题目描述直接到题解部分
题目
题目描述
一棵树上有 n 个节点,编号分别为 1 到 n,每个节点都有一个权值 w。
我们将以下面的形式来要求你对这棵树完成一些操作:
CHANGE u t
: 把结点 u 的权值改为 t。QMAX u v
: 询问从点 u 到点 v 的路径上的节点的最大权值。QSUM u v
: 询问从点 u 到点 v 的路径上的节点的权值和。
注意:从点 u 到点 v 的路径上的节点包括 u 和 v 本身。
输入格式
输入文件的第一行为一个整数 n,表示节点的个数。
接下来 n−1 行,每行 2 个整数 a 和 b,表示节点 a 和节点 b 之间有一条边相连。
接下来一行 n 个整数,第 i 个整数 w[i] 表示节点 i 的权值。
接下来 1 行,为一个整数 q,表示操作的总数。
接下来 q 行,每行一个操作,以 CHANGE u t
或者 QMAX u v
或者 QSUM u v
的形式给出。
输出格式
对于每个 QMAX
或者 QSUM
的操作,每行输出一个整数表示要求输出的结果。
样例
输入
4 1 2 2 3 4 1 4 2 1 3 12 QMAX 3 4 QMAX 3 3 QMAX 3 2 QMAX 2 3 QSUM 3 4 QSUM 2 1 CHANGE 1 5 QMAX 3 4 CHANGE 3 6 QMAX 3 4 QMAX 2 4 QSUM 3 4
输出
4 1 2 2 10 6 5 6 5 16
说明/提示
对于 100% 的数据,保证 1≤n≤3×10^4,0≤q≤2×10^5。
中途操作中保证每个节点的权值 w 在 −3×10^4 到 3×10^4 之间。
题解
树链剖分
今天偷个懒,这部分就自行理解一下啦(主要是改代码改烦了
其实有些东西自己看看,把代码敲完,把题改过的时候自然就懂了。
线段树
在这里我郑重地推荐一下zkw线段树,个人觉得超级好用,但我(懒得)不想写,这里附上我学习zkw的链接
强烈推荐!强烈推荐!强烈推荐!强烈推荐!强烈推荐!强烈推荐!强烈推荐!强烈推荐!
代码实现
//洛谷 P2590 [ZJOI2008]树的统计
#include<iostream>
#include<cstdio>
using namespace std;
const int N=30010;
int n;
int u,v;
int q;
int fa[N];//父亲
int dep[N];//深度
int siz[N];//子树节点数
int son[N];//重儿子
int top[N];//重链的顶部节点
int dfn[N];//节点的dfs序
int rnk[N];//dfs序对应的节点
int head[N];
int w[N];
int cnt;
int m;
struct edge{
int v;
int nex;
}a[N<<1];
struct node{
int v,maxx;
}b[N<<2];
void build1(int u,int v){
a[++cnt].v=v;
a[cnt].nex=head[u];
head[u]=cnt;
}
void dfs1(int u,int d){
siz[u]=1;
for(int i=head[u];i;i=a[i].nex){
int v=a[i].v;
if(!dep[v]){
dep[v]=d+1;
fa[v]=u;
dfs1(v,d+1);
siz[u]+=siz[v];
if(!son[u]||siz[v]>siz[son[u]]){
son[u]=v;
}
}
}
}
void dfs2(int u,int t){
top[u]=t;
dfn[u]=++cnt;
rnk[cnt]=u;
if(!son[u]){
return ;
}
dfs2(son[u],t);
for(int i=head[u];i;i=a[i].nex){
int v=a[i].v;
if(v!=son[u]&&v!=fa[u]){
dfs2(v,v);
}
}
}
void build2(){
for(m=1;m<n;m<<=1);
for(int i=m+1;i<=m+n;++i){
b[i].maxx=b[i].v=w[rnk[i-m]];
}
for(int i=m-1;i;--i){
b[i].maxx=max(b[i<<1].maxx,b[i<<1|1].maxx);
b[i].v=b[i<<1].v+b[i<<1|1].v;
}
}
int qsum(int s,int t){
int ans=0;
for(s=s+m-1,t=t+m+1;s^t^1;s>>=1,t>>=1){
if(~s&1){
ans+=b[s^1].v;
}
if(t&1){
ans+=b[t^1].v;
}
}
return ans;
}
int qmax(int s,int t){
int ans=-30000010;
for(s=s+m-1,t=t+m+1;s^t^1;s>>=1,t>>=1){
if(~s&1){
ans=max(ans,b[s^1].maxx);
}
if(t&1){
ans=max(ans,b[t^1].maxx);
}
}
return ans;
}
int qs(int x,int y){
int ans=0;
while(top[x]!=top[y]){
if(dep[top[x]]<dep[top[y]]){
swap(x,y);
}
ans+=qsum(dfn[top[x]],dfn[x]);
x=fa[top[x]];
}
if(dep[x]>dep[y]){
swap(x,y);
}
return ans+=qsum(dfn[x],dfn[y]);
}
int qm(int x,int y){
int ans=-30000010;
while(top[x]!=top[y]){
if(dep[top[x]]<dep[top[y]]){
swap(x,y);
}
ans=max(ans,qmax(dfn[top[x]],dfn[x]));
x=fa[top[x]];
}
if(dep[x]>dep[y]){
swap(x,y);
}
return ans=max(ans,qmax(dfn[x],dfn[y]));
}
void change(int x,int v){
x=m+dfn[x];
b[x].maxx=b[x].v=v;
while(x){
x>>=1;
b[x].v=b[x<<1].v+b[x<<1|1].v;
b[x].maxx=max(b[x<<1].maxx,b[x<<1|1].maxx);
}
}
int main(){
scanf("%d",&n);
for(int i=1;i<n;++i){
scanf("%d%d",&u,&v);
build1(u,v);
build1(v,u);
}
for(int i=1;i<=n;++i){
scanf("%d",&w[i]);
}
dep[1]=1;
dfs1(1,1);
cnt=0;
dfs2(1,1);
build2();
scanf("%d",&q);
for(int i=1;i<=q;++i){
char s[10];
scanf("%s%d%d",&s,&u,&v);
if(s[1]=='M'){
printf("%d\n",qm(u,v));
}
else if(s[1]=='S'){
printf("%d\n",qs(u,v));
}
else{
change(u,v);
}
}
return 0;
}