Processing math: 100%

题目描述

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

数据范围

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

题解

考虑 dpfi,j 表示 i 子树内的叶子节点到 i 的距离为 j 的最小代价, gi,j 表示 i 子树内的叶子节点到 fai 的距离为 j 的最小代价。

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

最优方案即为根节点的凸壳的最下端的点对应的取值,考虑以此递归下去得到方案:对于一个儿子 v ,如果 v 子树可自行解决,即无需修改 cv 就可符合要求,则直接下去做,否则将 cv 改为最接近的能使 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\n",ans);
    for (int i=2;i<=n;i++)
        printf("%lld\n",f[i]);
    return 0;
}
复制代码
posted @   xjqxjq  阅读(229)  评论(0编辑  收藏  举报
编辑推荐:
· DeepSeek 解答了困扰我五年的技术问题
· 为什么说在企业级应用开发中,后端往往是效率杀手?
· 用 C# 插值字符串处理器写一个 sscanf
· Java 中堆内存和栈内存上的数据分布和特点
· 开发中对象命名的一点思考
阅读排行:
· 为什么说在企业级应用开发中,后端往往是效率杀手?
· DeepSeek 解答了困扰我五年的技术问题。时代确实变了!
· 本地部署DeepSeek后,没有好看的交互界面怎么行!
· 趁着过年的时候手搓了一个低代码框架
· 推荐一个DeepSeek 大模型的免费 API 项目!兼容OpenAI接口!
点击右上角即可分享
微信分享提示