zoukankan      html  css  js  c++  java
  • 题目描述

    给定一棵大小为 $N$ ,以 $1$ 为根的有根树,每条边的初始权值是 $c_i$ ,单位修改代价是 $d_i$ 。将一条边 $i$ 的权值修改为 $X$ ( $X$ 必须为整数,但可以为负) 的代价为 $d_i × |c_i − X|$ 。
    你可以任意调整每条边的权值,使得从根节点到每个叶子的距离都相等,请你求出
    最小代价, 并输出一种方案。

    数据范围

    le N le 2 imes 10^5;1 le c_i,d_i le 10^6

    题解

    考虑 $dp$ , $f_{i,j}$ 表示 $i$ 子树内的叶子节点到 $i$ 的距离为 $j$ 的最小代价, $g_{i,j}$ 表示 $i$ 子树内的叶子节点到 $fa_i$ 的距离为 $j$ 的最小代价。

    发现 $f_i$ 和 $g_i$ 都是下凸壳, $f_i$ 是由 $g_v$ 合并起来的下凸壳, $g_i$ 是由 $f_i$ 先向右平移 $c_i$ ,然后把两端效率大于 $d_i$ 的直线斜率改成 $d_i$ 的下凸壳。发现平移很麻烦,可以先将叶子节点的下凸壳先全部平移到 $deep$ 的位置,然后每个位置维护两边斜率的变化值即可。

    最优方案即为根节点的凸壳的最下端的点对应的取值,考虑以此递归下去得到方案:对于一个儿子 $v$ ,如果 $v$ 子树可自行解决,即无需修改 $c_v$ 就可符合要求,则直接下去做,否则将 $c_v$ 改为最接近的能使 $v$ 子树自行解决的值。

    代码

    #include <bits/stdc++.h>
    typedef long long LL;
    using namespace std;
    const int N=2e5+5,M=N*70;
    const LL F=3e11; vector<int>g[N];
    int n,fa[N],T[N],t,ls[M],rs[M];
    LL dp[N],c[N],d[N],lf[N],rf[N],s[M],f[N],dt[N],ans;
    #define mid ((l+r)>>1)
    void upd(int &x,LL l,LL r,LL v,LL w){
        s[x=++t]=w;if (l==r) return;
        if (mid>=v) upd(ls[x],l,mid,v,w);
        else upd(rs[x],mid+1,r,v,w);
    }
    int merge(int x,int y){
        if (!x || !y) return (x|y);
        int z=++t;s[z]=s[x]+s[y];
        ls[z]=merge(ls[x],ls[y]);
        rs[z]=merge(rs[x],rs[y]);
        return z;
    }
    LL qL(int x,LL l,LL r,LL v){
        if (l==r) return l;s[x]-=v;
        if (s[ls[x]]>=v) return qL(ls[x],l,mid,v);
        else{
            v-=s[ls[x]];ls[x]=0;
            return qL(rs[x],mid+1,r,v);
        }
    }
    LL qR(int x,LL l,LL r,LL v){
        if (l==r) return l;s[x]-=v;
        if (s[rs[x]]>=v) return qR(rs[x],mid+1,r,v);
        else{
            v-=s[rs[x]];rs[x]=0;
            return qR(ls[x],l,mid,v);
        }
    }
    void solve(int u,LL x){
        int z=g[u].size();LL w;
        for (int v,i=0;i<z;i++){
            v=g[u][i];f[v]=c[v];w=x;
            if (lf[v]!=-1){
                if (w<lf[v])
                    f[v]-=lf[v]-w,w=lf[v];
                else if (w>rf[v])
                    f[v]+=w-rf[v],w=rf[v];
            }
            solve(v,w);
        }
    }
    LL Abs(LL x){return x>=0?x:-x;}
    int main(){
        scanf("%d",&n);
        for (int i=2,x;i<=n;i++)
            scanf("%d%d%d",&x,&c[i],&d[i]),
            g[x].push_back(i),dp[i]=dp[x]+c[i];
        for (int i=n,z;i;i--){
            if (g[i].empty()){
                lf[i]=rf[i]=dp[i];dt[i]=d[i];
                upd(T[i],0,F,dp[i],d[i]<<1);
                continue;
            }
            z=g[i].size();
            for (int v,j=0;j<z;j++)
                T[i]=merge(T[i],T[v=g[i][j]]),dt[i]+=dt[v];
            lf[i]=rf[i]=-1;if (i==1) break;
            if (dt[i]>d[i]){
                lf[i]=qL(T[i],0,F,dt[i]-d[i]);
                rf[i]=qR(T[i],0,F,dt[i]-d[i]);
                dt[i]=d[i];
            }
        }
        solve(1,qL(T[1],0,F,dt[1]));
        for (int i=2;i<=n;i++)
            ans+=Abs(f[i]-c[i])*d[i];
        printf("%lld
    ",ans);
        for (int i=2;i<=n;i++)
            printf("%lld
    ",f[i]);
        return 0;
    }
  • 相关阅读:
    TreeView使用集锦
    net 下安装、调试的常见问题与错误及解决方法 [转]
    Oracle中使用同义词
    再推荐两个blog和一首好歌
    一点感慨
    推荐一个blog和一个工具
    买书了
    第一次做饭
    ORA00911错误及解决方法
    C#3.0美文收集
  • 原文地址:https://www.cnblogs.com/xjqxjq/p/12047327.html
Copyright © 2011-2022 走看看