hdu 5692 dfs序 线段树
题目概括
给定一棵n个点的有根树,每个点有一个点权。根节点为0,节点标号为0~n-1。
定义最大路径为:从根出发走到某个点,点权和最大的路径。
现在有Q次操作,每种是以下两种之一:
(1).将点x的点权变成v。
(2).求经过某一个点的最大路径的点权和
参考博客 http://www.cnblogs.com/zhouzhendong/p/7263838.html
思路:
w[x]为节点x的权值 Dis[x]为从根节点到达当前节点的权值
那么,一开始,dis[x]可以通过一遍dfs全部求出来。
如何处理更改和询问呢?那么我们从询问入手
题目询问的是经过一个点的最大路径的点权和,那么其实就是到达这个节点或者其子孙节点的最大dis[x], 其实就是查询整个子树的max(dis[x])
在树的dfs序中,同一个子树节点的所对应的序号一定是整个dfs序中的连续的一段!
那么我们就可以把询问转化成“求区间最大值”的问题了。然而同理思考,那么修改其实就是修改整个子树的最大值!对于0 x y, 其实就是子树x的所有节点都增加y-w[x]!那么最大值也增加y-w[x]。
问题就变成了一个询问区间最大值和区间修改(同增或者同减)的问题了。
#include<bits/stdc++.h> using namespace std; #define pb push_back #define mp make_pair #define fi first #define se second #define ll long long #define lson l,mid,rt<<1 #define rson mid+1,r,rt<<1|1 const int N = 1E5+3; const int M = 1E5+3; const ll LINF = 1e17+3; int n,m; ll tmax[N<<2],col[N<<2]; int vis[N], tim,in[N],out[N]; ll v[N],d[N]; vector<int>V[N]; //距离d[N]需要注意 因为这里的1..n不是表示点1..n 而是dfs序的1..n void dfs(int pre,int t){ int len = V[t].size(); in[t]=++tim; d[in[t]] = d[in[pre]]+v[t]; for(int i=0;i<len;++i){ if(vis[V[t][i]])continue; vis[V[t][i]]=1; dfs(t,V[t][i]); } out[t]=tim; } void pushup(int rt){ tmax[rt] = max(tmax[rt<<1],tmax[rt<<1|1]); } void pushdown(int rt,int len){ if(col[rt]){ col[rt<<1] +=col[rt]; col[rt<<1|1]+=col[rt]; tmax[rt<<1]+=col[rt]; tmax[rt<<1|1]+=col[rt]; col[rt]=0; } } void build(int l,int r,int rt){ col[rt] = 0; if(l==r){ tmax[rt] = d[l]; return ; } int mid = (l+r)/2; build(lson);build(rson); pushup(rt); } void update(int l,int r,int rt,int a,int b ,ll val){ if(a<=l && b>=r){ col[rt]+=val; tmax[rt]+=val; return ; } pushdown(rt,r-l+1); int mid =(l+r)/2; if(a<=mid)update(lson,a,b,val); if(b>mid)update(rson,a,b,val); pushup(rt); } ll query(int l,int r,int rt,int a,int b){ if(a<=l && b>=r){ return tmax[rt]; } int mid =(l+r)/2; pushdown(rt,r-l+1); ll ans= -LINF; if(a<=mid) ans = max(ans, query(lson,a,b)); if(b>mid) ans = max(ans,query(rson,a,b)); return ans; } int main(){ int t; cin>>t; int cnt=1; while(t--){ tim= 0 ; memset(vis,0,sizeof(vis)); memset(col,0,sizeof(col)); scanf("%d %d",&n,&m); for(int i=0;i<=n;++i)V[i].clear(); for(int i=1;i<n;++i){ int u,v; scanf("%d %d",&u,&v); u++;v++; V[u].pb(v); V[v].pb(u); } for(int i=1;i<=n;++i){ scanf("%lld",&v[i]); d[i]=v[i]; } vis[1]=1; d[0]=0; dfs(0,1); build(1,n,1); printf("Case #%d:\n",cnt++); while(m--){ int c,x;ll y; scanf("%d",&c); if(c==0){ //修改 成Y 也就是将x以及所有子树修改成Y 也就是增加 y-v[x] , v[x]=y; scanf("%d %lld",&x,&y);x++; update(1,n,1,in[x],out[x],y-v[x]); v[x]=y; } else { //询问到x scanf("%d",&x);x++; printf("%lld\n",query(1,n,1,in[x],out[x])); } } } return 0; }