【三中校内训练】旅行
【题解】
显然的这是一道树形DP的题目
这里令f[i][0]为从i出发向以它为根的子树里走直到不能走的最大、最小价值
(不能走是什么自己阅读题目)
令s为x的儿子,w[i][j]为i和j之间的边的长度,则
f[x][0]=max(f[s][1]+w[s][i])
f[x][1]=min(f[s][0]+w[s][i])
显然的通过这种方法,我们可以得到60分
可是,如何优化到线性呢
我们考虑一个节点x,x向某条边走的情况出现了很多次,浪费了很多时间
我们定义h[x][0/1]为从x出发,经过x的父亲fa走到不能走为止的最大、最小价值
显然的h[x][0]会被x的兄弟节点y的f[y][1]和h[fa][1]影响,
h[x][1]会被x的兄弟节点y的f[y][0]和x父亲fa的h[fa][0]影响,
我们要从中选取一个最大的或者最小的计入答案
但是为了防止被自己这颗子树更新,我们需要求一个次小和次大
以x为根的树的答案可以顺便处理,具体看代码
#include<stdio.h> #include<algorithm> #include<string.h> #define mod 1000000007 #define il inline using namespace std; const int N=1000001; typedef long long ll; struct edge{int next,to,val;} e[N]; int n,M,g[N]; ll f[N][2],p[N][2],ans[N]; il void addedge(int x,int y,int z){ e[++M]=(edge){g[x],y,z};g[x]=M; } il void dfs1(int h,int fa){ f[h][1]=(1ll<<50);f[h][0]=0; for(int i=g[h];i;i=e[i].next){ if(e[i].to==fa) continue; dfs1(e[i].to,h); f[h][0]=max(f[h][0],f[e[i].to][1]+e[i].val); f[h][1]=min(f[h][1],f[e[i].to][0]+e[i].val); } if(f[h][1]==(1ll<<50)) f[h][1]=0; } il void dfs2(int h,int fa){ ll s1=(1ll<<50),s2=(1ll<<50),l1=0,l2=0,x,y; for(int i=g[h];i;i=e[i].next){ if(e[i].to==fa) continue; x=f[e[i].to][1]+e[i].val; y=f[e[i].to][0]+e[i].val; if(x>l1){l2=l1;l1=x;} else if(x>l2) l2=x; if(y<s1){s2=s1;s1=y;} else if(y<s2) s2=y; } if(h>1){ if(p[h][0]>l1){l2=l1;l1=p[h][0];} else if(p[h][0]>l2) l2=p[h][0]; if(p[h][1]<s1){s2=s1;s1=p[h][1];} else if(p[h][1]<s2) s2=p[h][1]; } for(int i=g[h];i;i=e[i].next){ if(e[i].to==fa) continue; x=f[e[i].to][1]+e[i].val; y=f[e[i].to][0]+e[i].val; if(x==l1) p[e[i].to][1]=l2+e[i].val; else p[e[i].to][1]=l1+e[i].val; if(y==s1) p[e[i].to][0]=s2+e[i].val; else p[e[i].to][0]=s1+e[i].val; dfs2(e[i].to,h); } ans[h]=l1; } int main(){ freopen("travel.in","r",stdin); freopen("travel.out","w",stdout); scanf("%d",&n); for(int i=1,x,y,z;i<n;i++){ scanf("%d%d%d",&x,&y,&z); addedge(x,y,z); addedge(y,x,z); } dfs1(1,0);dfs2(1,0); for(int i=1;i<=n;i++) printf("%lld\n",ans[i]); return 0; }
蜉蝣渴望着飞翔,尽管黄昏将至