zoukankan      html  css  js  c++  java
  • G 火山哥周游世界(树上走过确切k个点的最短时间,树形dp)

    题:https://ac.nowcoder.com/acm/contest/4114/G

    题意:给定一棵树,给定m个确定的点,问从1~n为起点开始,走过着m个点的最短距离是多少(不用回来)

    分析:考虑根为1的情况,那么答案就是(所有1到m个点的边距离和)*2-(1到最远点的距离),因为不用回来;

       考虑换根,假设节点 i 到其子树的确定点的距离和的2倍为sum[i],在第一次dfs过程中我们可以算出每一个的sum[i],然后将sum[i]转义成以 i 为根到m个点的边的距离和的2倍;

       关键是考虑换根后sum[i]的父亲边的更新:先令:sum[i]=sum[fa[i]] 若 i 子树中有确定点,那么-=2*w,若父亲边有确定点,那么+=2*w;

       重要的一步是 i 到最远点距离的更新,假设更新fa->i , T节点为fa为根的最远节点;

       · 要是fa->i 不是fa->T的路径,那么 i 的最远距离即为fa的最远距离+w(i与fa的边权)

       ·否则,说明fa的最远距离给占用,那么肯定是w+x,根据贪心,这个x必须为当fa为根节点时离fa次远的距离;

       所以就需要维护最远距离fir和次远距离sec。

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    #define pb push_back
    #define MP make_pair
    #define pil pair<int,ll>
    const int inf=0x3f3f3f3f;
    const ll INF=1e18;
    const int M=5e5+5;
    vector<pil >g[M];
    int n,m,sz[M],nex[M];
    ll fir[M],sec[M],sum[M];
    void upd(ll x,int u,int v){
        if(x>=fir[u]){
            sec[u]=fir[u],fir[u]=x;
            nex[u]=v;
        }
        else
            sec[u]=max(sec[u],x);
    }
    void dfs1(int u,int fa){
        for(auto it:g[u]){
            int v=it.first;
            ll w=it.second;
            if(v!=fa){
                dfs1(v,u);
                sz[u]+=sz[v];
                if(sz[v]){
                    sum[u]+=sum[v]+2ll*w;
                    upd(fir[v]+w*(sz[v]!=0),u,v);
                }
    
            }
        }
    }
    void dfs2(int u,int fa){
        for(auto it:g[u]){
            int v=it.first;
            ll w=it.second;
            if(v!=fa){
                sum[v]=sum[u]-2ll*w*(sz[v]!=0)+2ll*w*(m-sz[v]!=0);
                if(sz[v]!=m){
                    if(v==nex[u])
                        upd(sec[u]+w,v,0);
                    else
                        upd(fir[u]+w,v,0);
                }
    
                dfs2(v,u);
            }
        }
    }
    int main(){
        scanf("%d%d",&n,&m);
        for(int i=1;i<n;i++){
            int u,v;
            ll w;
            scanf("%d%d%lld",&u,&v,&w);
            g[u].pb(MP(v,w));
            g[v].pb(MP(u,w));
        }
        for(int x,i=1;i<=m;i++){
            scanf("%d",&x);
            sz[x]=1;
        }
        dfs1(1,0);
    
        dfs2(1,0);
    
        for(int i=1;i<=n;i++)
            printf("%lld
    ",sum[i]-fir[i]);
        return 0;
    }
    View Code
  • 相关阅读:
    备份
    Android资料之-EditText中的inputType
    trim() 是什么意思?
    两数相加
    点击edittext并显示其内容
    php 返回上一页并刷新
    sql one
    sql 语句 查询两个字段都相同的方法
    我为什么喜欢Go语言123123
    数据字典
  • 原文地址:https://www.cnblogs.com/starve/p/13611226.html
Copyright © 2011-2022 走看看