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

解题思路:

我们把关键点构成的树的总长叫做虚树,设虚树直径两端为a,b
很容易发现从一个点i出发的最小代价为“(虚树的总路程+i到虚树的距离)*2-max(dis(i,a),dis(i,b))”.
四遍dfs即可。

#include<bits/stdc++.h>
#define ll long long
using namespace std;

int getint()
{
    int i=0,f=1;char c;
    for(c=getchar();(c!='-')&&(c<'0'||c>'9');c=getchar());
    if(c=='-')f=-1,c=getchar();
    for(;c>='0'&&c<='9';c=getchar())i=(i<<3)+(i<<1)+c-'0';
    return i*f;
}

const int N=500005;
int n,m,rt;
ll totlen,dis1[N],dis2[N];
int tot,first[N],to[N<<1],nxt[N<<1],w[N<<1],size[N],key[N],in[N];

void add(int x,int y,int z)
{
    nxt[++tot]=first[x],first[x]=tot,to[tot]=y,w[tot]=z;
}

void dfs1(int u,int fa)
{
    size[u]=key[u];
    for(int e=first[u];e;e=nxt[e])
    {
        int v=to[e];
        if(v==fa)continue;
        dis1[v]=dis1[u]+w[e];
        if(key[v]&&dis1[v]>dis1[rt])rt=v;
        dfs1(v,u);
        if(size[v])totlen+=w[e];
        size[u]+=size[v];
    }
    in[u]=(size[u]>0);
}

void dfs2(int u,int fa,ll *dis)
{
    for(int e=first[u];e;e=nxt[e])
    {
        int v=to[e];
        if(v==fa)continue;
        dis[v]=dis[u]+w[e];
        if(key[v]&&dis[v]>dis[rt])rt=v;
        dfs2(v,u,dis);
    }
}

void dfs3(int u,int fa)
{
    for(int e=first[u];e;e=nxt[e])
    {
        int v=to[e];
        if(v==fa)continue;
        dis2[v]=(in[v]?0:dis2[u]+w[e]);
        dfs3(v,u);
    }
}

int main()
{
    //freopen("lx.in","r",stdin);
    n=getint(),m=getint();
    if(!m)
    {
        for(int i=1;i<=n;i++)puts("0");
        return 0;
    }
    for(int i=1;i<n;i++)
    {
        int x=getint(),y=getint(),z=getint();
        add(x,y,z),add(y,x,z);
    }
    for(int i=1;i<=m;i++)key[rt=getint()]=1;
    dfs1(rt,0);
    memset(dis1,0,sizeof(dis1));
    dfs2(rt,0,dis1),dfs2(rt,0,dis2);
    for(int i=1;i<=n;i++)dis1[i]=max(dis1[i],dis2[i]);
    memset(dis2,0,sizeof(dis2));
    dfs3(rt,0);
    for(int i=1;i<=n;i++)
        printf("%lld\n",totlen*2+dis2[i]*2-dis1[i]);
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值