Loading

P6419-Kamp-题解

P6419-Kamp

题意:在一棵树上的节点处可能是某个人的家,求任意一个节点举行聚会将所有人送回家的最小距离。

题解:很明显的换根dp,但是corner case十分多,首先我们要求出在1节点举办聚会时将所有人送回家并且返回原点的距离。题面说的”分别送回“属实有点歧义,一开始还以为每次只能接一个人,,

若可以承载多人,则只需考虑每个子树上有没有目的地,若有则去往子树的分支路径需要经过两遍(来回),可以求出dp[1].

然后这玩意换根dp后可以求出每个节点开始送人回家并且最后返回根节点的距离。

\[dp[son]=dp[now]-(siz[v]!=0)*2*w+(n-siz[v]!=0)*2*w \]

由于送完最后一个人后司机不一定要开回根节点,那么可以减去的最长的距离是每个节点作为根的时候距离最大的目的地。这也是一个换根dp。有

\[fa[son]=max((fa[now]+w1)*(fa[now]!=-inf),shen[now]+w1) \]

fa[i]表示i节点的父节点的路径上目的地的最大距离,没有目的地的时候应当设为-inf。但是由于父节点子树上距离最大的目的地可能就在son节点的子树上,所以需要同时维护至少两条分支上的最长距离,也就是维护最大值和次大值,修改后的转移方程如下:

\[fa[son]=max(fa[now]+w1,(biao[now][0]!=son)?shen[now][0]+w1:shen[now][1]+w1) \]

设tak[now]为当前节点的距离最大的目的地,有

\[tak[son]=max(fa[v],shen[son][0]-dep[son]) \]

答案即为dp[now]-tak[now]。

#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define int ll
const int maxn=5e5+10;
const ll inf=1e16;
int n,k;
bool hav[maxn];
int dp[maxn],siz[maxn],shen[maxn][2],dep[maxn],tak[maxn],biao[maxn][2],fa[maxn];
struct node{
    int v;int w;
};
vector<node> yuan[maxn];
void dfs(int now,int fa){
    if(hav[now]) shen[now][0]=dep[now],biao[now][0]=now,siz[now]=1;
    for(int i=0;i<yuan[now].size();i++){
        int v=yuan[now][i].v,w1=yuan[now][i].w;
        if(v==fa) continue;
        dep[v]=dep[now]+w1;
        dfs(v,now);
        siz[now]+=siz[v];
        dp[now]+=(dp[v]+(siz[v]?1:0)*2LL*w1);
        if(!biao[v][0]) continue;
        if(biao[now][0]&&biao[now][1]){
            if(shen[v][0]>shen[now][0]){
                shen[now][1]=shen[now][0],biao[now][1]=biao[now][0];
                shen[now][0]=shen[v][0],biao[now][0]=v;
            }
            else if(shen[v][0]<=shen[now][0]&&shen[v][0]>=shen[now][1]){
                shen[now][1]=shen[v][0];biao[now][1]=v;
            }
            else continue;
        }
        else if(biao[now][0]){
            if(shen[v][0]>shen[now][0]){
                shen[now][1]=shen[now][0],biao[now][1]=biao[now][0];
                shen[now][0]=shen[v][0],biao[now][0]=v;
            }
            else shen[now][1]=shen[v][0],biao[now][1]=v;
        }
        else{
            shen[now][0]=shen[v][0],biao[now][0]=v;
        }
    }
}
void dfs1(int now,int fat){
    for(int i=0;i<yuan[now].size();i++){
        int v=yuan[now][i].v,w1=yuan[now][i].w;
        if(v==fat) continue;
        dp[v]=dp[now]+((k-siz[v])?1:0)*2LL*w1-2LL*w1*(siz[v]?1:0);
        if(hav[v]) fa[v]=0;
        fa[v]=max(fa[v],fa[now]+w1);
        if(biao[now][0]&&biao[now][1]){
            if(biao[now][0]==v) fa[v]=max(fa[v],shen[now][1]-dep[now]+w1);
            else fa[v]=max(fa[v],shen[now][0]-dep[now]+w1);
        }
        else if(biao[now][0]&&!biao[now][1]){
            if(biao[now][0]!=v) fa[v]=max(fa[v],shen[now][0]-dep[now]+w1);
        }
        dfs1(v,now);
    }
}
signed main(){
    cin>>n>>k;
    for(int i=1;i<n;i++){
        int a1,a2,a3;cin>>a1>>a2>>a3;
        yuan[a1].push_back((node){a2,a3});
        yuan[a2].push_back((node){a1,a3});
    }
    for(int i=1;i<=k;i++){
        int a1;cin>>a1;hav[a1]=1;
    }
    for(int i=0;i<=n;i++) fa[i]=-inf;
    if(hav[1]) fa[1]=0;
    dfs(1,0);
    dfs1(1,0);
    for(int i=1;i<=n;i++) tak[i]=max(fa[i],shen[i][0]-dep[i]);
    for(int i=1;i<=n;i++) cout<<dp[i]-tak[i]<<endl;
    return 0;
}
posted @ 2021-05-29 11:07  14long  阅读(67)  评论(0)    收藏  举报