zoukankan      html  css  js  c++  java
  • Luogu P1131 时态同步

    头一次看到这道题的时候被题面吓到了......因为我焊电路板的时候全是毛刺XD

    我写的方法是类似于贪心,看到Luogu上好多大佬写树上DP......orz
    题意简述:

    给定一颗边权为非负整数的树,通过增加(只能增加)一些边的边权,使根节点到每个叶节点的路径上的边权和相等,在此基础上,求最小的边权和该变量。

    思路:

    首先可以看出来,如果某几条边分别都要增加一个量,而这些边又都是从同一个公共父节点发出来的,那么不如把这个增加量转移到它们公共父节点的边上。

    这样就可以将许多变化量集中为一个,层层向上推得出最优解。

    如下图:

    所以:

    首先预处理,找出所有子节点中离根节点最远的那一个,记录其距离为 $ maxn $ ,作为所有节点的目标距离。

    然后,对于每一个叶节点 $ x $ ,连接它和它的父节点之间那条边的增加量即为 $ maxn-dis[x] $ ,即所要增加的差值。

    在DFS过程中,对于某个节点 $ x $ :

    设 $ v $ 为 $ x $ 的子节点

    设连接 $ v $ 与 为 $ x $ 的边为 $ S $

    设 $ ad[v] $ 为S期望的增加量

    则有:

    $ ad[x]=Σ_v min(ad[x],ad[v]) $

    即:找出所有通向儿子的边中,最小的增加量,将这份增加量转移到父节点和(父节点的父节点)之间的边上。

    同时还要把所有通向儿子的边的增加量,减去这个转移走的量。

    最后答案就是整个图中,所有边增加量的总和。

    代码:

    
    #include<iostream>
    #include<algorithm>
    #include<cstdio>
    #include<cstring>
    #include<queue>
    #define LL long long
    using namespace std;
    int n,s;
    int out[500050];
    int u[500050],v[500050],w[500050],head[500050],nxt[500050],cnt=0;
    void add(int ui,int vi,int wi){
        cnt++;
        nxt[cnt]=head[ui];
        head[ui]=cnt;
        u[cnt]=ui;
        v[cnt]=vi;
        w[cnt]=wi;	
    }
    LL d[500050];
    LL maxn=0;
    void init(int ui,int fa){
        for(int i(head[ui]);i;i=nxt[i]){
            int vi=v[i];
            int wi=w[i];
            if(vi==fa)continue;
            d[vi]=d[ui]+wi;
            maxn=max(maxn,d[vi]);
            init(vi,ui);
        }
    }
    
    LL ad[500050];
    void dfs(int ui,int fa){
        
        for(int i(head[ui]);i;i=nxt[i]){
            int vi=v[i];
            int wi=w[i];
            if(vi==fa)continue;
            dfs(vi,ui);
            if(i==head[ui])ad[ui]=ad[vi];
            ad[ui]=min(ad[ui],ad[vi]);
        }
        if(ui==s)return;
        //printf("ad[%d]=%d
    ",ui,ad[ui]);
        for(int i(head[ui]);i;i=nxt[i]){
            int vi=v[i];
            if(vi==fa)continue;
            ad[vi]-=ad[ui];
            //printf("ad[%d]-=ad[%d]
    ",vi,ui);
        }
    }
    int main(){
        scanf("%d%d",&n,&s);
        for(int i(1);i<=n-1;i++){
            int ui,vi,wi;scanf("%d%d%d",&ui,&vi,&wi);
            add(ui,vi,wi);
            add(vi,ui,wi);
            out[ui]++;
            out[vi]++;
        }
        init(s,0);
        for(int i(1);i<=n;i++){
            if(out[i]==1){
                ad[i]=maxn-d[i];
                continue;
            }
            d[i]=0;
        }
        
        LL ans=0;
        dfs(s,0);
        for(int i(1);i<=n;i++)ans+=ad[i];
        printf("%lld",ans);
        return 0;
    }
    
    
  • 相关阅读:
    【04】Vue 之 事件处理
    【03】Vue 之列表渲染及条件渲染
    【02】 Vue 之 数据绑定
    传递参数的模式最适合向函数传入大量可先参数的情形
    ie6 PNG图片透明
    实现表单的输入框当光标为当前时,去掉默认值
    SSIS ->> Environment Variables
    SQL Server ->> FileTable
    SQL Server ->> 间接实现COUNT(DISTINCT XXX) OVER(PARTITION BY YYY)
    SQL Server ->> EXECUTE AS LOGIN/USER和Revert表达式
  • 原文地址:https://www.cnblogs.com/soul-M/p/9627536.html
Copyright © 2011-2022 走看看