problem
百度科技园内有n个零食机,零食机之间通过n−1条路相互连通。每个零食机都有一个值v,表示为小度熊提供零食的价值。
由于零食被频繁的消耗和补充,零食机的价值v会时常发生变化。小度熊只能从编号为0的零食机出发,并且每个零食机至多经过一次。另外,小度熊会对某个零食机的零食有所偏爱,要求路线上必须有那个零食机。
为小度熊规划一个路线,使得路线上的价值总和最大。
Input
输入数据第一行是一个整数T(T≤10),表示有T组测试数据。
对于每组数据,包含两个整数n,m(1≤n,m≤100000),表示有n个零食机,m次操作。
接下来n−1行,每行两个整数x和y,(0≤x,y
Output
对于每组数据,首先输出一行”Case #?:”,在问号处应填入当前数据的组数,组数从1开始计算。
对于每次询问,输出从编号为0的零食机出发,必须经过编号为xx零食机的路线中,价值总和的最大值。
Sample Input
1
6 5
0 1
1 2
0 3
3 4
5 3
7 -5 100 20 -5 -7
1 1
1 3
0 2 -1
1 1
1 5
Sample Output
Case #1:
102
27
2
20
思路
先把这棵树dfs求出dfs序以建成线段树,in[x],out[x]分别表示x的子树节点的左右区间。
维护一个presum[]作为一开始每个节点到根节点的权值和
后面的update直接调整change=val-cost[]值即可
理解:树上节点的DFS序满足父系节点区间覆盖子系节点。因此可以用in,out这个区间来对线段树修改,实际上修改的就是这个结点的子树(一个点权值改变,它的子树到根的路径权值和跟着改变)。对于本题,val[rt]维护的是经过该节点的权值最大路径,即题目所求
所以,对于修改,对区间内点的val进行加减;对于询问,只要求出询问结点区间内所有结点的val最大值即可。
可能不理解的地方:如果取值都为正,那自然到叶子结点了。这里权值是可能为负的,所以不一定到哪。对于每一个询问,找到in和out即找到了它的子节点们,因此可以用线段树来维护最大值,且这个最大值对应的路径一定经过询问结点:-D。
dfs代码:
void dfs(int rt,int f){
in[rt]=++Clock;
presum[rt]=presum[f]+cost[rt];//维护当前节点到根节点的权值和
Hash[Clock]=rt;//将树中的编号hash成dfs序的映射
//因为dfs时用树的编号易求前缀和presum
//然后hash后 方便后面线段树调用presum
for(int i=head[rt];i!=-1;i=edges[i].to){
int tt=edges[i].from;
if(tt!=f) dfs(tt,rt);
}
out[rt]=Clock;
}
代码示例
#include<bits/stdc++.h>
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
using namespace std;
typedef __int64 ll;
const int maxn=100100;
struct Edge{
int from,to;
}edges[maxn<<1];
int head[maxn<<1],in[maxn],out[maxn],Hash[maxn];
int Clock,cnt;
ll presum[maxn],cost[maxn];
ll lazy[maxn<<2],val[maxn<<2];//val维护区间最值 lazy标记
void edge_add(int u,int v){
edges[cnt].from=v;
edges[cnt].to=head[u];
head[u]=cnt++;
}
void init(){
memset(head,-1,sizeof(head));
memset(Hash,0,sizeof(Hash));
memset(presum,0,sizeof(presum));
memset(cost,0,sizeof(cost));
memset(lazy,0,sizeof(lazy));
memset(val,0,sizeof(val));
memset(edges,0,sizeof(edges));
cnt=0;
Clock=0;
}
void dfs(int rt,int f){
in[rt]=++Clock;
presum[rt]=presum[f]+cost[rt];//维护当前节点到根节点的权值和
Hash[Clock]=rt;//将树中的编号hash成dfs序的映射
//因为dfs时用树的编号易求前缀和presum
//然后hash后 方便后面线段树调用presum
for(int i=head[rt];i!=-1;i=edges[i].to){
int tt=edges[i].from;
if(tt!=f) dfs(tt,rt);
}
out[rt]=Clock;
}
void PushUp(int rt){
val[rt]=max(val[rt<<1],val[rt<<1|1]);
}
void PushDown(int rt){
if(lazy[rt]){
lazy[rt<<1]+=lazy[rt];
lazy[rt<<1|1]+=lazy[rt];
val[rt<<1]+=lazy[rt];
val[rt<<1|1]+=lazy[rt];
lazy[rt]=0;
}
}
void build(int l,int r,int rt){
val[rt]=lazy[rt]=0;
if(l==r){
val[rt]=presum[Hash[l]];//赋初值,表示每个点的ans(到根的点权和)
return ;
}
int m=(l+r)>>1;
build(lson);
build(rson);
PushUp(rt);//最后push_up
}
void update(ll v,int L,int R,int l,int r,int rt){
if(l>R||r<L) return ;
if(L<=l&&R>=r){
val[rt]+=v;
lazy[rt]+=v;
return ;
}
PushDown(rt);//下面要往下递归,所以要释放标记
int m=(l+r)>>1;
update(v,L,R,lson);
update(v,L,R,rson);
PushUp(rt);
}
ll Query(int L,int R,int l,int r,int rt){
if(L<=l&&R>=r) return val[rt];
PushDown(rt);
ll ans=-1e18;
int m=(l+r)>>1;
if(L<=m) ans=max(ans,Query(L,R,lson));
if(R>m) ans=max(ans,Query(L,R,rson));
return ans;
}
int main()
{
int t,n,m;
scanf("%d",&t);
for(int kase=1;kase<=t;kase++){//节点从1编号
printf("Case #%d:\n",kase);
init();
scanf("%d %d",&n,&m);
int u,v,x,op;
ll val;
for(int i=1;i<n;++i){
scanf("%d %d",&u,&v);
u++,v++;//节点从1编号
edge_add(u,v);
edge_add(v,u);
}
for(int i=1;i<=n;++i) scanf("%I64d",&cost[i]);
presum[0]=0;
dfs(1,0);
build(1,Clock,1);
for(int i=0;i<m;++i){
scanf("%d",&op);
if(op==1){
scanf("%d",&x);
x++;//节点从1编号
printf("%I64d\n",Query(in[x],out[x],1,Clock,1));
}
else{
scanf("%d %I64d",&x,&val);
x++;//节点从1编号
ll change=val-cost[x];
update(change,in[x],out[x],1,Clock,1);
cost[x]=val;
}
}
}
return 0;
}