一、题目链接:
二、题目大意:
一棵树上有 n n n个节点,编号分别为 1 ∼ n 1\sim n 1∼n,每个节点都有一个权值 w w w。我们将以下面有 q q q个询问,要求你对这棵树完成一些操作:
CHANGE u t
: 把结点 u u u的权值改为 t t t。QMAX u v
: 询问从点 u u u到点 v v v的路径上的节点的最大权值。QSUM u v
: 询问从点 u u u到点 v v v的路径上的节点的权值和。
数据范围: 1 ≤ n ≤ 3 × 1 0 4 1\leq n\leq 3×10^4 1≤n≤3×104, 0 ≤ q ≤ 2 × 1 0 5 0\leq q\leq 2×10^5 0≤q≤2×105
三、题目分析
- 题目的意思非常简单明了,很明显是一道类似于线段树的题目。但是这个时候我们遇到的麻烦就是,线段树一般是在一段连续区间上进行的,而本题的数据在一棵树上,这对我们使用线段树带来了麻烦。
- 因为题目每一次修改都是在某一条路径上进行的,所以这提醒我们可以将树上某一条链上看作连续的一段数据,这为我们使用线段树创造了条件。但同时,如果选择一条链为连续数据,那么就意味着剩下的点将不是连续的数据,所以如何选择这条链成为了关键问题。
- 为了降低时间复杂度,我们应当选择子节点最多的点来构成这条链,即树链剖分算法。所以我们可以使用树链剖分的方式来维护这些数据,将重链上的点重新进行编号,让它们作为连续的一段数据,使得我们可以用线段树来维护区间和和区间最大值。
- 当询问区间值的时候,我们将区间切割为不同树链的组合,分段来得到答案即可。
四、正解程序:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define inf 1e15
using namespace std;
typedef long long ll;
const ll maxn=30010;
struct node
{
ll v;
ll next;
}e[maxn*2];
ll p[maxn],t=0;
ll n,val[maxn];
ll d[maxn],fa[maxn],size[maxn],son[maxn];
void insert(ll u,ll v)
{
e[t].v=v;
e[t].next=p[u];
p[u]=t++;
}
void dfs1(ll u)//树链剖分
{
size[u]=1;
for(int i=p[u];i!=-1;i=e[i].next)
{
int v=e[i].v;
if(fa[u]!=v)
{
fa[v]=u;
d[v]=d[u]+1;
dfs1(v);
size[u]+=size[v];
if(size[son[u]]<size[v])
son[u]=v;
}
}
}
ll id[maxn],weight[maxn],top[maxn];
ll times=0;
void dfs2(ll u,ll topf)
{
id[u]=++times;
weight[times]=val[u];
top[u]=topf;
if(!son[u])
return;
dfs2(son[u],topf);
for(ll i=p[u];i!=-1;i=e[i].next)
{
ll v=e[i].v;
if(v!=fa[u] && v!=son[u])
dfs2(v,v);
}
}
ll tree[4*maxn][2];
void pushup(ll pos)//线段树部分
{
tree[pos][0]=max(tree[pos<<1][0],tree[pos<<1|1][0]);
tree[pos][1]=tree[pos<<1][1]+tree[pos<<1|1][1];
}
void build(ll pos,ll l,ll r)
{
if(l==r)
{
tree[pos][0]=tree[pos][1]=weight[l];
return;
}
ll mid=(l+r)>>1;
build(pos<<1,l,mid);
build(pos<<1|1,mid+1,r);
pushup(pos);
}
void change(ll pos,ll l,ll r,ll aim,ll num)
{
if(l==r)
{
tree[pos][0]=tree[pos][1]=num;
return;
}
ll mid=(l+r)>>1;
if(aim<=mid)
change(pos<<1,l,mid,aim,num);
else
change(pos<<1|1,mid+1,r,aim,num);
pushup(pos);
}
ll query(ll pos,ll l,ll r,ll s,ll e,ll flag)
{
if(s<=l && r<=e)
return tree[pos][flag];
ll mid=(l+r)>>1,t1,t2;
if(!flag)
t1=t2=-inf;
else
t1=t2=0;
if(s<=mid)
t1=query(pos<<1,l,mid,s,e,flag);
if(e>mid)
t2=query(pos<<1|1,mid+1,r,s,e,flag);
if(!flag)
return max(t1,t2);
return t1+t2;
}
int main()
{
memset(p,-1,sizeof(p));
scanf("%lld",&n);
for(ll i=1;i<=n-1;i++)
{
ll u,v;
scanf("%lld%lld",&u,&v);
insert(u,v);
insert(v,u);
}
for(ll i=1;i<=n;i++)
scanf("%lld",&val[i]);
dfs1(1);
dfs2(1,1);
build(1,1,times);
ll q;
scanf("%lld",&q);
for(ll i=1;i<=q;i++)
{
char str[10];
ll u,v;
scanf("%s%lld%lld",str,&u,&v);
if(strcmp(str,"CHANGE")==0)
change(1,1,times,id[u],v);
else if(strcmp(str,"QMAX")==0)
{
ll ans=-inf;
while(top[u]!=top[v])
{
if(d[top[u]]<d[top[v]])
swap(u,v);
ll res=query(1,1,times,id[top[u]],id[u],0);
u=fa[top[u]];
ans=max(ans,res);
}
if(d[u]>d[v])
swap(u,v);
ll res=query(1,1,times,id[u],id[v],0);
ans=max(ans,res);
printf("%lld\n",ans);
}
else
{
ll ans=0;
while(top[u]!=top[v])
{
if(d[top[u]]<d[top[v]])
swap(u,v);
ll res=query(1,1,times,id[top[u]],id[u],1);
u=fa[top[u]];
ans+=res;
}
if(d[u]>d[v])
swap(u,v);
ll res=query(1,1,times,id[u],id[v],1);
ans+=res;
printf("%lld\n",ans);
}
}
return 0;
}