题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5692
Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)
Problem Description
百度科技园内有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<n),表示编号为x的零食机与编号为y的零食机相连。
接下来一行由n个数组成,表示从编号为0到编号为n−1的零食机的初始价值v(|v|<100000)。
接下来m行,有两种操作:0 x y,表示编号为x的零食机的价值变为y;1 x,表示询问从编号为0的零食机出发,必须经过编号为x零食机的路线中,价值总和的最大值。
本题可能栈溢出,辛苦同学们提交语言选择c++,并在代码的第一行加上:
`#pragma comment(linker, "/STACK:1024000000,1024000000") `
Output
对于每组数据,首先输出一行”Case #?:”,在问号处应填入当前数据的组数,组数从1开始计算。
对于每次询问,输出从编号为0的零食机出发,必须经过编号为x零食机的路线中,价值总和的最大值。
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
题解:
题目给出n个点和n-1条边,并且是连通图,那么显然是一棵树;
一棵无向树,我们可以任意取一个节点作为树根,根据题意取编号为0的节点为树根;
那么,对于其他的1~n-1号节点,从树根(0号节点)到它们只有唯一一条路径,
假设sum[i]代表从0号节点到i号节点这条路径上所有的点(包括节点0和i)的value值之和;
(换句话说,假设从root=0,到i节点的唯一一条简单路径为0-1-3-5-i,那么 sum[i] = value[0] + value[1] + value[3] + value[5] + value[i];)
then,对于题目中描述的两种操作:
①修改节点i的value[i]值:
一旦修改value[i],会影响到以i为树根的子树内的所有节点的sum[],
假设value[i]+=k,那么其正科统领的一整棵子树上的节点上的sum[]都要加上k;
②查询:从0号节点出发,经过节点x,走一条简单路径,所经过的所有节点的value值之和的最大值:
显然,这就是枚举节点x统领的子树上所有的节点的sum[]值,找到其中最大的就行;
(注:在上面,节点x统领的子树内所有的点,包含节点x自己)
那么,如果我们使用DFS序把整棵树“拍平”,把他们排列到一串序列中……
这个序列中,节点x的in[x]和out[x]代表:[ in[x] , out[x] ]区间内的点正好是节点x统领的子树内的所有节点;
那么对于上面两种操作,我们从可以从“树上修改,树上查询”变成“区间修改,区间查询”……
即:
①x节点统领的子树内,所有节点的sum[]+=k → [ in[x] , out[x] ]区间内所有节点的sum[]+=k;
②x节点统领的子树内,查询所有节点中sum[]的最大值 → 查询[ in[x] , out[x] ]区间内所有节点的sum[]最大值;
显然,如果使用线段树进行维护,两种操作就都从O(n)时间复杂度变成了O(lgn)的时间复杂度;
AC代码:
#include<bits/stdc++.h> using namespace std; typedef long long LL; const int maxn=100000+5; const LL INF=1e18; int n,m; LL val[maxn],sum[maxn]; //邻接表-st struct Edge{ int u,v; Edge(int u,int v){this->u=u,this->v=v;} }; vector<Edge> E; vector<int> G[maxn]; void Adjacency_List_Init(int l,int r) { E.clear(); for(int i=l;i<=r;i++) G[i].clear(); } void addedge(int u,int v) { E.push_back(Edge(u,v)); E.push_back(Edge(v,u)); int _size=E.size(); G[u].push_back(_size-2); G[v].push_back(_size-1); } //邻接表-ed //dfs序-st int dfs_clock; bool vis[maxn]; int in[maxn],out[maxn]; int peg[maxn]; inline void DFS_Init() { dfs_clock=0; memset(vis,0,sizeof(vis)); } void dfs(int now) { in[now]=++dfs_clock; peg[in[now]]=now; vis[now]=1; for(int i=0,_size=G[now].size();i<_size;i++) { int nxt=E[G[now][i]].v; if(!vis[nxt]) { sum[nxt]=sum[now]+val[nxt]; dfs(nxt); } } out[now]=dfs_clock; } //dfs序-ed //线段树-st struct Node{ int l,r; LL val,lazy; void update(LL x) { val+=x; lazy+=x; } }node[4*maxn]; void pushdown(int root) { if(node[root].lazy) { node[root*2].update(node[root].lazy); node[root*2+1].update(node[root].lazy); node[root].lazy=0; } } void pushup(int root) { node[root].val=max(node[root*2].val,node[root*2+1].val); } void build(int root,int l,int r) { node[root].l=l; node[root].r=r; node[root].val=0; node[root].lazy=0; if(l==r) node[root].val=sum[peg[l]]; else { int mid=l+(r-l)/2; build(root*2,l,mid); build(root*2+1,mid+1,r); pushup(root); } } void update(int root,int st,int ed,int val) { if(st>node[root].r || ed<node[root].l) return; if(st<=node[root].l && node[root].r<=ed) node[root].update(val); else { pushdown(root); update(root*2,st,ed,val); update(root*2+1,st,ed,val); pushup(root); } } LL query(int root,int st,int ed) { if(ed<node[root].l || node[root].r<st) return -INF; if(st<=node[root].l && node[root].r<=ed) return node[root].val; else { pushdown(root); LL lson=query(root*2,st,ed); LL rson=query(root*2+1,st,ed); pushup(root); return max(lson,rson); } } //线段树-ed int main() { int t; scanf("%d",&t); for(int kase=1;kase<=t;kase++) { scanf("%d%d",&n,&m); Adjacency_List_Init(0,n-1); for(int i=1,u,v;i<=n-1;i++) { scanf("%d%d",&u,&v); addedge(u,v); } for(int i=0;i<n;i++) scanf("%I64d",&val[i]); DFS_Init(); sum[0]=val[0]; dfs(0); build(1,1,n); printf("Case #%d: ",kase); for(int i=1,type,x,y;i<=m;i++) { scanf("%d",&type); if(type==0) { scanf("%d%d",&x,&y); update(1,in[x],out[x],y-val[x]); val[x]=y; } if(type==1) { scanf("%d",&x); printf("%I64d ",query(1,in[x],out[x])); } } } }
注意:这里的线段树,是区间更新(一个区间加上一个值),区间查询(一个区间的val维护:区间内所有节点最大值)。