题目描述
给定一棵大小为 $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; }