zoukankan      html  css  js  c++  java
  • [Bzoj3743][Coci2015] Kamp【换根Dp】

    Online JudgeBzoj3743

    Label:换根Dp,维护最长/次长链

    题目描述

    一颗树n个点,n-1条边,经过每条边都要花费一定的时间,任意两个点都是联通的。

    有K个人(分布在K个不同的点)要集中到一个点举行聚会。

    聚会结束后需要一辆车从举行聚会的这点出发,把这K个人分别送回去。

    请你回答,对于i=1~n,如果在第i个点举行聚会,司机最少需要多少时间把K个人都送回家。

    输入

    第一行两个数,n,K。

    接下来n-1行,每行三个数,x,y,z表示x到y之间有一条需要花费z时间的边。

    接下来K行,每行一个数,表示K个人的分布。

    输出

    输出n个数,第i行的数表示:如果在第i个点举行聚会,司机需要的最少时间。

    样例

    Input

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

    Output

    2
    

    题解

    对于根节点做聚餐点的情况很容易得出。

    (ans[root]=2*∑^{K}_{i=1}dep[person[i]]-max(dep[person[1..K]]))

    也就是,所有住处的深度之和*2(要往返到其他住处)(-)住处的最大深度(可以选择最后去深度最大的那个点,这样它就不用了计两次了)。这样求根节点做聚餐点的情况只用dfs一遍,找一个住处的最大深度即可。


    求其他非根节点做聚餐点的情况。

    对于它子树内的情况和上面一个求法,但对于子树外很明显要用到换根Dp​。维护一个最长/次长链即可,具体细节见代码。

    #include<bits/stdc++.h>
    #define int long long
    using namespace std;
    const int N=500100;
    inline int read(){
        int x=0;char c=getchar();
        while(c<'0'||c>'9')c=getchar();
        while(c>='0'&&c<='9')x=(x<<3)+(x<<1)+(c^48),c=getchar();
        return x;
    }
    struct edge{
        int to,nxt,d;
    }e[2*N];
    int head[N],Ecnt,n,m;
    inline void link(int u,int v,int d){
        e[++Ecnt].to=v,e[Ecnt].d=d,e[Ecnt].nxt=head[u];
        head[u]=Ecnt;
    }
    int cnt[N],son[N],ma1[N],ma2[N],len[N];
    /*
    cnt[]:子树中住处的个数
    ma1/2[]:最长/次长链长度(在第一遍dfs中只包含子树中的链,第二遍dfs中考虑了子树外的链)
    son[]:i的最长链由i的哪个儿子son引上来(在第二遍dfs中,如果最长链发生改变,则将son赋值为0,表示在子树外)
    len[]:住处的深度总和(第二遍dfs时算上子树外住处离自己的距离总和)
    
    */
    bool mark[N];
    void dfs(int x,int fa){ //第一遍,处理子树
        if(mark[x])cnt[x]=1;
        for(register int i=head[x];i;i=e[i].nxt){
            int y=e[i].to;if(y==fa)continue;
            dfs(y,x);
            cnt[x]+=cnt[y],len[x]+=len[y];
            if(!cnt[y])continue;
            len[x]+=e[i].d;
            int nowLink=ma1[y]+e[i].d;
            if(nowLink>=ma1[x]){
                ma2[x]=ma1[x],ma1[x]=nowLink,
                son[x]=y;
            }
            else if(nowLink>=ma2[x])ma2[x]=nowLink;
        }
    }
    void redfs(int x,int fa){//第二遍,换根。处理子树外
        for(register int i=head[x];i;i=e[i].nxt){
            int y=e[i].to;if(y==fa)continue;
    		//将以x为根的所有状态转移到以y为根 
            len[y]=len[x]; 
            if(cnt[y]==0)len[y]+=e[i].d;//y离所有标记点远了
            if(cnt[y]==m)len[y]-=e[i].d;//y离所有标记点近了	
            int nowLink=(y==son[x]?ma2[x]:ma1[x])+e[i].d;
            if(cnt[y]!=m){//!!!注意
                if(nowLink>=ma1[y]){
                    ma2[y]=ma1[y],ma1[y]=nowLink;
                    son[y]=0;//y此时的最长链在它的子树外 
                }
                else if(nowLink>=ma2[y])ma2[y]=nowLink;
            }
            redfs(y,x);
        }
    }
    signed main(){
        n=read(),m=read();
        for(register int i=1;i<n;i++){
            int u=read(),v=read(),d=read(); 
            link(u,v,d),link(v,u,d);
        }
        for(register int i=1;i<=m;i++)mark[read()]=1;
        dfs(1,0);
        redfs(1,0);
        for(register int i=1;i<=n;i++)printf("%lld
    ",2*len[i]-ma1[i]);
        return 0;
    }
    

    update:

    一开始下面这个操作没有判((cnt[y]!=m))但在bzoj上也A了,后来做到了重题[COCI2014/2015 Contest#1 F]发现会被下面这个数据搞掉。

    if(cnt[y]!=m){//!!!注意
       if(nowLink>=ma1[y]){
       		ma2[y]=ma1[y],ma1[y]=nowLink;
       		son[y]=0;//y此时的最长链在它的子树外 
    	}
     	else if(nowLink>=ma2[y])ma2[y]=nowLink;
    }
    /*
    输入:
    5 2
    2 5 1
    2 4 1
    1 2 2
    1 3 2
    4 
    5
    正确输出:
    5
    3
    7
    2
    2
    */
    
  • 相关阅读:
    jmeter的断言
    Fiddler(五)设置显式IP地址
    学习pycharm----自动化接口
    fidder重复创建数据+模拟接口响应数据+fidder接口测试
    python网络/并发编程部分简单整理
    python面向对象部分简单整理
    python模块与包简单整理
    python函数部分整理
    Python基础部分整理
    Scheme Implementations对比
  • 原文地址:https://www.cnblogs.com/Tieechal/p/11522970.html
Copyright © 2011-2022 走看看