首页 > 代码库 > 【三中校内训练】旅行

【三中校内训练】旅行

技术分享

 技术分享

技术分享

 

 

 【题解】

显然的这是一道树形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;
}

 技术分享

 

【三中校内训练】旅行