zoukankan      html  css  js  c++  java
  • sss

    <更新提示>

    <第一次更新>


    <正文>

    kamp

    Description

    jz 市的云台山是个很美丽的景区,小 x 暑期到云台山打工,他的任务是开景区的大巴。
    云台山景区有 N 个景点,这 N 个景点由 N-1 条道路连接而成,我们保证这 N 个景点之 间有且仅有一条路径相连,并且每条道路开车经过的时间不一定相同。
    现在小 x 会提前知道有 K 个人要坐车,并且每个人都有一个景点作为目标景点,小 x 必须要把每个人送到他们要去的景点。
    不过,小 x 可以指定这 K 个人去某个景点集合,这样他就从这个集合点出发,分别去 送这 K 个的人。
    现在小 x 想知道,选哪个点集合最省时间,当然,为了更综合的判断,他想知道,以 每个点作为集合点,花费的最小时间是多少?

    Input Format

    第一行两个整数 N 和 K。
    接下来 N-1 行,每行 3 个整数,Xi,Yi 和 Vi,表示从第 Xi 个景点到第 Yi 个景点有一 条道路相连,这条道路消耗的时间是 Vi。 Xi 和 Yi 在[1..N]范围内,Vi 在[1..100000]之间。
    接下来 K 行,每行一个整数 Zi,表示第 i 个人要去的景点。

    Output Format

    N 行,第 i 行输出为 Zi,表示以第 i 个景点作为集合地点,把 K 队人送到各自要去的景 点花费的最小总时间,并且小 x 送完最后一个人后,他的任务就完成了。

    Sample Input

    5 2 
    2 5 1 
    2 4 1 
    1 2 2 
    1 3 2 
    4 
    5
    

    Sample Output

    5 
    3 
    7 
    2 
    2
    

    解析

    很容易想到是树形(dp),并且要换根。

    可以先设计出普通的(dp),设(f_{x})代表以(x)为根的子树中送完所有人并回到(x)的路程和。为什么要这样设计,因为这样方便转移,我们再设一个(g_x)代表以(x)为根的子树中送完所有人不回到(x)的路程和,就可以列出状态转移方程了。

    [f_x=sum_{yin son(x)}f_y+2 imes e(x,y)\g_x=f_x-max_{yin son(x)}{f_y+e(x,y)-g_y} ]

    那么根据题意,答案就是(g_x),应该比较好理解,那么我们就得到了一个(O(n^2))的做法。

    (Code:)

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 2020;
    struct edge { int ver,val,next; } e[N*2];
    int n,k,t,Head[N],sum[N],tar[N],f[N],g[N];
    inline void insert(int x,int y,int v) { e[++t] = (edge){y,v,Head[x]} , Head[x] = t; }
    inline void input(void)
    {
        scanf("%d%d",&n,&k);
        for (int i=1;i<n;i++)
        {
            int x,y,v;
            scanf("%d%d%d",&x,&y,&v);
            insert( x , y , v );
            insert( y , x , v );
        }
        for (int i=1;i<=k;i++)
        {
            int x; scanf("%d",&x);
            tar[x]++;
        }
    }
    inline void dfs(int x,int fa)
    {
        int Max = 0; sum[x] = tar[x];
        for (int i=Head[x];i;i=e[i].next)
        {
            int y = e[i].ver;
            if ( y == fa ) continue;
            dfs( y , x );
            sum[x] += sum[y];
            if ( !sum[y] ) continue;
            f[x] += f[y] + 2 * e[i].val;
            Max = max( Max , f[y] + e[i].val - g[y] );
        }
        g[x] = f[x] - Max;
    }
    int main(void)
    {
        freopen("kamp.in","r",stdin);
        freopen("kamp.out","w",stdout);
        input();
        for (int i=1;i<=n;i++)
        {
            memset( f , 0 , sizeof f );
            memset( g , 0 , sizeof g );
            memset( sum , 0 , sizeof sum );
            dfs( i , 0 );
            printf("%d
    ",g[i]);
        }
        return 0;
    }
    
    

    然后直接考虑换根即可。状态(g)的形式是很复杂的,我们不妨进行化简,令:

    [g_x=f_x-g'_x ]

    那么就有$$g_x=f_x-max_{yin son(x)}{f_y+e(x,y)-g_y}=f_x-max_{yin son(x)}{f_y+e(x,y)-f_y+g'y}=f_x-max{yin son(x)}{e(x,y)+g'_y}$$

    又有$$g_x=max_{yin son(x)}{e(x,y)+g'_y}$$

    (ok),事实上(g'_x)的意义也就是以(x)出发,走向(x)子树中最远关键点的距离,最后要的答案(g_x=f_x+g'_x)

    我们发现(g'_x)很容易换根求,只需要考虑向下走的最长链和向上走的最长链,记录一下最大值和次大值来更新即可。

    那么问题就转换为了求(f_x)。其实考虑一下路径的形态很容易得到换根方程:

    [f_y=egin{cases}f_x& exists kin subtree(y)\f_x-2 imes e(x,y)& forall kin subtree(y)\f_x+2 imes e(x,y) & forall k otin subtree(y) end{cases} ]

    其中(k)代表关键点,于是就可以(O(n))求了。

    (Code:)

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 500020;
    struct edge { int ver,val,next; } e[N*2];
    int n,k,t,Head[N],sum[N],tar[N],p[N],root,deg[N];
    long long f[N],d[N][2],g[N],_f[N];
    inline void insert(int x,int y,int v) { e[++t] = (edge){y,v,Head[x]} , Head[x] = t; }
    inline void input(void)
    {
        scanf("%d%d",&n,&k);
        for (int i=1;i<n;i++)
        {
            int x,y,v;
            scanf("%d%d%d",&x,&y,&v);
            insert( x , y , v );
            insert( y , x , v );
            deg[x]++ , deg[y]++;
        }
        for (int i=1;i<=k;i++)
        {
            int x; scanf("%d",&x);
            tar[x]++;
        }
    }
    inline void dfs(int x,int fa)
    {
        sum[x] = tar[x];
        for (int i=Head[x];i;i=e[i].next)
        {
            int y = e[i].ver;
            if ( y == fa ) continue;
            dfs( y , x );
            sum[x] += sum[y];
            if ( !sum[y] ) continue;
            f[x] += f[y] + 2LL * e[i].val;
            long long val = d[y][1] + e[i].val;
            if ( val >= d[x][1] )
                d[x][0] = d[x][1] , d[x][1] = val , p[x] = y;
            else if ( val > d[x][0] ) d[x][0] = val;
        }
    }
    inline void dp(int x,int fa)
    {
        for (int i=Head[x];i;i=e[i].next)
        {
            int y = e[i].ver;
            if ( y == fa ) continue;
            _f[y] = _f[x] - ( k - sum[y] == 0 ) * 2LL * e[i].val;
            _f[y] += 2LL * e[i].val * ( sum[y] == 0 );
            if ( p[x] != y )
                g[y] = max( g[y] , d[x][1] + e[i].val );
            else g[y] = max( g[y] , d[x][0] + e[i].val );
            g[y] = max( g[y] , g[x] + e[i].val );
            dp( y , x );
        }
    }
    inline void findroot(void)
    {
        for (int i=1;i<=n;i++)
    		if ( tar[i] ) root = i;
    }
    int main(void)
    {
        freopen("kamp.in","r",stdin);
        freopen("kamp.out","w",stdout);
        input();
        findroot();
        dfs( root , 0 );
        _f[root] = f[root];
        dp( root , 0 );
        for (int i=1;i<=n;i++)
            printf("%lld
    ",_f[i]-max(g[i],d[i][1]));
        return 0;
    }
    
    
    
    

    <后记>

  • 相关阅读:
    8 组合
    7 继承与派生
    防止伪造跨站请求(转)
    HTML5 拖拽效果实现
    yii2.0 文件上传
    div光标定位问题总结
    可编辑DIV (contenteditable="true") 在鼠标光标处插入图片或者文字
    Linux下搭建Nginx+MySQL+PHP
    GitHub初体验
    Mac下配置Apache + Php + Mysql环境
  • 原文地址:https://www.cnblogs.com/Parsnip/p/11389569.html
Copyright © 2011-2022 走看看