[bzoj3743 Coci2015] Kamp(树形dp)

[bzoj3743 Coci2015] Kamp(树形dp)

传送门

Description

一颗树n个点,n-1条边,经过每条边都要花费一定的时间,任意两个点都是联通的。
有K个人(分布在K个不同的点)要集中到一个点举行聚会。
聚会结束后需要一辆车从举行聚会的这点出发,把这K个人分别送回去。
请你回答,对于i=1~n,如果在第i个点举行聚会,司机最少需要多少时间把K个人都送回家。

Input

第一行两个数,n,K。
接下来n-1行,每行三个数,x,y,z表示x到y之间有一条需要花费z时间的边。
接下来K行,每行一个数,表示K个人的分布。

Output

输出n个数,第i行的数表示:如果在第i个点举行聚会,司机需要的最少时间。

Sample Input

7 2

1 2 4

1 3 1

2 5 1

2 4 2

4 7 3

4 6 2

3

7

Sample Output

11

15

10

13

16

15

10

HINT

【数据规模】

K <= N <= 500000

1 <= x,y <= N, 1 <= z <= 1000000

Solution

总体上是用全部的路程减去最大的一条路程
先两次dfs求出每个点全部路程
再两次dfs求出最大一条路径

Code

//By Menteur_Hxy
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#define M(a,b) memset(a,(b),sizeof(a))
#define F(i,a,b) for(register int i=(a);i<=(b);i++)
#define E(i,u) for(register int i=head[u];i;i=nxt[i])
using namespace std;
typedef long long LL;

int read() {
    int x=0,f=1; char c=getchar();
    while(!isdigit(c)) {if(c=='-')f=-f; c=getchar();}
    while(isdigit(c)) x=(x<<1)+(x<<3)+c-48,c=getchar();
    return x*f;
}

const int N=500010;
const LL INF=0x7fffffffffffffff;
int n,k,cnt;
int nxt[N<<1],to[N<<1],w[N<<1],head[N],siz[N];
LL dis[N],down[N][2],up[N];

void dfs1(int u,int pre) { //向下 
    E(i,u) { int v=to[i];
        if(v==pre) continue;
        dfs1(v,u); siz[u]+=siz[v];
        dis[u]+=dis[v]+(siz[v]?w[i]:0);
    }
}

void dfs2(int u,int pre) { //向上 
    E(i,u) { int v=to[i];
        if(v==pre) continue;
        dis[v]=dis[u];
//      if(siz[v])dis[v]-=w[i];
//        if(siz[v]<k)dis[v]+=w[i];
        if(siz[v]==k) dis[v]-=w[i];
        else if(!siz[v]) dis[v]+=w[i];
        dfs2(v,u);
    }
} 

void dfs3(int u,int pre) { //向下 
    down[u][0]=down[u][1]=(siz[u]?0:-INF);
    E(i,u) { int v=to[i];
        if(v==pre) continue;
        dfs3(v,u);
        if(down[v][0]+w[i]>down[u][0]) down[u][1]=down[u][0],down[u][0]=down[v][0]+w[i];
        else if(down[v][0]+w[i]>down[u][1]) down[u][1]=down[v][0]+w[i];
    }
}

void dfs4(int u,int pre) { //向上 
    E(i,u) { int v=to[i];
        if(v==pre) continue;
        if(down[v][0]+w[i]==down[u][0]) up[v]=down[u][1]+w[i];
        else up[v]=down[u][0]+w[i];
        up[v]=max(up[v],up[u]+w[i]);
        dfs4(v,u);
    }
}

#define add(a,b,c) nxt[++cnt]=head[a],to[cnt]=b,w[cnt]=c,head[a]=cnt
int main() {
    n=read(),k=read();
    F(i,1,n-1) {
        int a=read(),b=read(),c=read();
        add(a,b,c),add(b,a,c);
    }
    F(i,1,k) siz[read()]=1;
    dfs1(1,0); dfs2(1,0); dfs3(1,0); 
    up[1]=(siz[1]?0:-INF); dfs4(1,0);
//  F(i,1,n) printf("%lld ",2*dis[i]);cout<<endl;
    F(i,1,n) printf("%lld\n",2*dis[i]-max(up[i],down[i][0]));
    return 0;
}
posted @ 2018-07-25 19:45 Menteur_Hxy 阅读( ...) 评论( ...) 编辑 收藏
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值