【题目描述】
一棵树上有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本身
【输入格式】
输入文件的第一行为一个整数n,表示节点的个数。
接下来n – 1行,每行2个整数a和b,表示节点a和节点b之间有一条边相连。
接下来一行n个整数,第i个整数wi表示节点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
【题意分析】
ZJOI真是暴力!!
又见暴力树剖模板,我们需要两个线段树:一个记录区间最大值,一个保留区间和。
因为是单点修改所以连lazytag都不需要啦!
最近发现我的快读老是爆炸,索性都不打快读了。
这道题给你的权值会是负数,蒟蒻写querymax函数时一开始return 0,读题好久才在题面底部发现权值可以是负数,于是改成return -INF。
就过啦!!!!
树剖程序长,做的是细心。
Code:
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#define MAX 100000
#define INF 1 << 29
using namespace std;
struct Front_Link_Star{
int next,to;
}edge[MAX];
int sum[MAX << 2],maxx[MAX << 2],id[MAX],top[MAX],father[MAX],depth[MAX];
int size[MAX],rk[MAX],a[MAX],head[MAX],son[MAX],cnt,dfn,n,m;
//sum[]记录区间和,maxx[]记录区间最大值
inline void Add_Edge(int u,int v){
edge[++cnt].to=v;
edge[cnt].next=head[u];
head[u]=cnt;
} //链式前向星
inline void push_up(int now){
sum[now]=sum[now << 1]+sum[now << 1|1];
maxx[now]=max(maxx[now << 1],maxx[now << 1|1]);
} //上传
inline void build(int now,int tl,int tr){
if (tl==tr){
sum[now]=maxx[now]=rk[tl];
return;
}
int mid=(tl+tr) >> 1;
build(now << 1,tl,mid);
build(now << 1|1,mid+1,tr);
push_up(now);
} //建树
inline void update(int now,int tl,int tr,int left,int right,int change){
if (right<tl||tr<left)return;
if (left<=tl&&tr<=right){
sum[now]=maxx[now]=change;
return;
}
int mid=(tl+tr) >> 1;
update(now << 1,tl,mid,left,right,change);
update(now << 1|1,mid+1,tr,left,right,change);
push_up(now);
} //单点修改
inline int querymax(int now,int tl,int tr,int left,int right){
if (right<tl||tr<left)return -INF;
//就是这里,我原来打了return 0,因为0会比负数大,所以要打-INF
if (left<=tl&&tr<=right)return maxx[now];
int mid=(tl+tr) >> 1;
int ans=-INF;
ans=max(ans,querymax(now << 1,tl,mid,left,right));
ans=max(ans,querymax(now << 1|1,mid+1,tr,left,right));
return ans;
} //查询区间最大值
inline int querysum(int now,int tl,int tr,int left,int right){
if (right<tl||tr<left)return 0;
if (left<=tl&&tr<=right)return sum[now];
int mid=(tl+tr) >> 1;
int ans=0;
ans+=querysum(now << 1,tl,mid,left,right);
ans+=querysum(now << 1|1,mid+1,tr,left,right);
return ans;
} //查询区间和
inline int Query_Max(int x,int y){
int ans=-INF;
while (top[x]!=top[y]){
if (depth[top[x]]<depth[top[y]])swap(x,y);
ans=max(ans,querymax(1,1,n,id[top[x]],id[x]));
x=father[top[x]];
}
if (depth[x]>depth[y])swap(x,y);
ans=max(ans,querymax(1,1,n,id[x],id[y]));
return ans;
} //查询路径最大值
inline int Query_Sum(int x,int y){
int ans=0;
while (top[x]!=top[y]){
if (depth[top[x]]<depth[top[y]])swap(x,y);
ans+=querysum(1,1,n,id[top[x]],id[x]);
x=father[top[x]];
}
if (depth[x]>depth[y])swap(x,y);
ans+=querysum(1,1,n,id[x],id[y]);
return ans;
} //查询路径和
inline void DFS1(int now,int fa,int d){
father[now]=fa;
depth[now]=d;
size[now]=1;
int maxson=-1;
for (register int i=head[now];i;i=edge[i].next){
int v=edge[i].to;
if (v==fa)continue;
DFS1(v,now,d+1);
size[now]+=size[v];
if (maxson<size[v]){
maxson=size[v];
son[now]=v;
}
}
}
inline void DFS2(int now,int top_heavy){
top[now]=top_heavy;
id[now]=++dfn;
rk[dfn]=a[now];
if (!son[now])return;
DFS2(son[now],top_heavy);
for (register int i=head[now];i;i=edge[i].next){
int v=edge[i].to;
if (v!=father[now]&&v!=son[now])DFS2(v,v);
}
} //树剖
int main(){
scanf("%d",&n);
for (register int i=1;i<=n-1;i++){
int x,y;
scanf("%d%d",&x,&y);
Add_Edge(x,y);
Add_Edge(y,x);
}
for (register int i=1;i<=n;i++)
scanf("%d",&a[i]);
DFS1(1,0,1);
DFS2(1,1);
build(1,1,n);
scanf("%d",&m);
while (m--){
int x,y;
char s[10];
scanf("%s%d%d",&s,&x,&y);
if (s[1]=='M')printf("%d\n",Query_Max(x,y));
if (s[1]=='S')printf("%d\n",Query_Sum(x,y));
if (s[1]=='H')update(1,1,n,id[x],id[x],y);
//这里是单点修改
}
return 0;
}