O - Snacks(DFS序)
Posted cherish-lin
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了O - Snacks(DFS序)相关的知识,希望对你有一定的参考价值。
百度科技园内有nn个零食机,零食机之间通过n−1n−1条路相互连通。每个零食机都有一个值vv,表示为小度熊提供零食的价值。
由于零食被频繁的消耗和补充,零食机的价值vv会时常发生变化。小度熊只能从编号为0的零食机出发,并且每个零食机至多经过一次。另外,小度熊会对某个零食机的零食有所偏爱,要求路线上必须有那个零食机。
为小度熊规划一个路线,使得路线上的价值总和最大。
由于零食被频繁的消耗和补充,零食机的价值vv会时常发生变化。小度熊只能从编号为0的零食机出发,并且每个零食机至多经过一次。另外,小度熊会对某个零食机的零食有所偏爱,要求路线上必须有那个零食机。
为小度熊规划一个路线,使得路线上的价值总和最大。
Input输入数据第一行是一个整数T(T≤10)T(T≤10),表示有TT组测试数据。
对于每组数据,包含两个整数n,m(1≤n,m≤100000)n,m(1≤n,m≤100000),表示有nn个零食机,mm次操作。
接下来n−1n−1行,每行两个整数xx和y(0≤x,y<n)y(0≤x,y<n),表示编号为xx的零食机与编号为yy的零食机相连。
接下来一行由nn个数组成,表示从编号为0到编号为n−1n−1的零食机的初始价值v(|v|<100000)v(|v|<100000)。
接下来mm行,有两种操作:0 x y0 x y,表示编号为xx的零食机的价值变为yy;1 x1 x,表示询问从编号为0的零食机出发,必须经过编号为xx零食机的路线中,价值总和的最大值。
本题可能栈溢出,辛苦同学们提交语言选择c++,并在代码的第一行加上:
`#pragma comment(linker, "/STACK:1024000000,1024000000") `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标记每个点的时间戳,并且记录到每个点的路径(同时开一个Rev数组,从时间戳返回到原节点,建线段树的时候要用到).
知道了每个节点的时间戳,就知道了该节点的子树的区间,为[Start[x],End[x]],更新的话用线段树区间更新,查询的话用线段树区间查询,用到lazy数组.
#pragma comment(linker, "/STACK:1024000000,1024000000") #include<bits/stdc++.h> using namespace std; typedef long long ll; const int maxn=100010; const ll INF=0x3f3f3f3f3f3f3f3f;//这里一定是long long 啊啊啊啊 ll head[maxn],dis[maxn],w[maxn],Rev[maxn]; int Start[maxn],End[maxn]; int tot,cnt,n,m; ll sum[maxn<<2],lazy[maxn<<2]; struct node ll v,next; e[maxn<<1]; void add(int u,int v) e[tot]=(node)v,head[u]; head[u]=tot++; void DFS(int u,int pre) Start[u]=++cnt; Rev[cnt]=u; for(int i=head[u];i!=-1;i=e[i].next) int v=e[i].v; if(v==pre)continue; dis[v]=dis[u]+w[v]; DFS(v,u); End[u]=cnt; void pushup(int rt) sum[rt]=max(sum[rt*2],sum[rt*2+1]); void pushdown(int rt) if(lazy[rt]) lazy[rt<<1]+=lazy[rt]; lazy[rt<<1|1]+=lazy[rt]; sum[rt<<1]+=lazy[rt]; sum[rt<<1|1]+=lazy[rt]; lazy[rt]=0; void build(int l,int r,int rt) lazy[rt]=0; if(l==r) sum[rt]=dis[Rev[l]]; return ; int mid=(l+r)/2; build(l,mid,rt*2); build(mid+1,r,rt*2+1); pushup(rt); void update(int L,int R,int w,int l,int r,int rt)//[L,R]区间加上w if(L<=l&&R>=r) sum[rt]+=w; lazy[rt]+=w; return ; pushdown(rt); int mid=(l+r)>>1; if(L<=mid)update(L,R,w,l,mid,rt<<1); if(R>=mid+1)update(L,R,w,mid+1,r,rt<<1|1); pushup(rt); ll querymax(int L,int R,int l,int r,int rt) if(L<=l&&R>=r) return sum[rt]; pushdown(rt); ll ans=-INF; int mid=(l+r)>>1; if(L<=mid)ans=max(ans,querymax(L,R,l,mid,rt<<1)); if(R>=mid+1)ans=max(ans,querymax(L,R,mid+1,r,rt<<1|1)); return ans; int main() int T,k=1; scanf("%d",&T); while(T--) cnt=tot=0; scanf("%d%d",&n,&m); memset(head,-1,sizeof(head)); for(int i=1;i<=n-1;i++) int u,v; scanf("%d%d",&u,&v); u++; v++; add(u,v); add(v,u); for(int i=1;i<=n;i++)scanf("%lld",&w[i]); dis[1]=w[1]; DFS(1,-1); build(1,n,1); printf("Case #%d:\n", k++); while(m--) int op,x,y; scanf("%d%d",&op,&x); x++; if(op==1) printf("%lld\n",querymax(Start[x],End[x],1,n,1)); else scanf("%d",&y); update(Start[x],End[x],y-w[x],1,n,1); w[x]=y; return 0;
以上是关于O - Snacks(DFS序)的主要内容,如果未能解决你的问题,请参考以下文章
HDU 5692 - Snacks - [DFS序+线段树]