bzoj3743【题解】Kamp

题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3743

这道题第一想法是暴力。

但是没有细分数据点。

硬想了半天没有思路。

只好去搜题解。但是题解看着好麻烦的。

于是就综合了很多题解的思路。

选择用两次树形DP

第一次先求一些必要的值。

把第一个关键点设为根。从它开始DP。

第二次也是一样,以第一个为根,更新答案。

细节可以看注释。

如下。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=500005;
int n,k,tot,head[maxn],d[maxn];
bool have[maxn<<1];
struct node{
    int nxt,dis,to;
    #define nxt(x) e[x].nxt
    #define dis(x) e[x].dis
    #define to(x) e[x].to
}e[maxn<<1];
inline void add(int from,int to,int v){
    to(++tot)=to;dis(tot)=v;
    nxt(tot)=head[from];head[from]=tot;
}
int num[maxn];//以它为根的子树中有多少关键点。
ll f[2][maxn],val[maxn];//f[0][x]表示从x向下走到下一个关键点的最大长,f[1][x]是次大
inline void doo1(int now,int fa){
    num[now]=have[now];
    for(int i=head[now];i;i=nxt(i)){
        int t=to(i);
        if(t==fa) continue;
        doo1(t,now);
        if(!num[t]) continue;
        num[now]+=num[t];
        val[now]+=val[t]+dis(i);
        int tmp=f[0][t]+dis(i);
        if(tmp>f[0][now]){ 
            f[1][now]=f[0][now];
            f[0][now]=tmp;
        }else if(tmp>f[1][now])
                f[1][now]=tmp;
    }
}
ll ans[maxn];//答案
inline void push(int now,int fa){
    for(int i=head[now];i;i=nxt(i)){
        int t=to(i);
        if(t==fa) continue;
        ans[t]=ans[now]+dis(i);
        push(t,now);
    }
}
//up表示now到上一个关键点的距离
inline void doo2(int now,int fa,ll up,ll sum){
    ans[now]=2*sum-max(up,f[0][now]);//更新答案,画图可以得到
    for(int i=head[now];i;i=nxt(i)){
        int t=to(i);
        if(t==fa) continue;
        if(num[t]){//如果to是关键点求up
            ll tmp=0;
            if(up||have[now]) tmp=max(tmp,up+dis(i));
            if(f[0][now]==f[0][t]+dis(i)){
                if(f[1][now])
                    tmp=max(tmp,f[1][now]+dis(i));
            }else
                tmp=max(tmp,f[0][now]+dis(i));
            doo2(t,now,tmp,sum);
        }else{//直接求然后下放
            ans[t]=ans[now]+dis(i);
            push(t,now);
        }
    }
}
int main()
{
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n-1;i++){
        int x,y,z;
        scanf("%d%d%d",&x,&y,&z);
        add(x,y,z);add(y,x,z);
    }
    for(int i=1;i<=k;i++)
        scanf("%d",&d[i]),have[d[i]]=1;
    doo1(d[1],0);
    doo2(d[1],0,0,val[d[1]]);
    for(int i=1;i<=n;i++)
        printf("%lld\n",ans[i]);
    system("pause");
    return 0;
}

 

posted @ 2019-07-29 11:12  ChrisKKK  阅读(238)  评论(0编辑  收藏  举报