树
题目描述
给定一棵大小为 N ,以 1 为根的有根树,每条边的初始权值是 ci ,单位修改代价是 di 。将一条边 i 的权值修改为 X ( X 必须为整数,但可以为负) 的代价为 di×|ci−X| 。
你可以任意调整每条边的权值,使得从根节点到每个叶子的距离都相等,请你求出
最小代价, 并输出一种方案。
数据范围
\ le N \le 2 \times 10^5;1 \le c_i,d_i \le 10^6
题解
考虑 dp , fi,j 表示 i 子树内的叶子节点到 i 的距离为 j 的最小代价, gi,j 表示 i 子树内的叶子节点到 fai 的距离为 j 的最小代价。
发现 fi 和 gi 都是下凸壳, 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; }
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 解答了困扰我五年的技术问题
· 为什么说在企业级应用开发中,后端往往是效率杀手?
· 用 C# 插值字符串处理器写一个 sscanf
· Java 中堆内存和栈内存上的数据分布和特点
· 开发中对象命名的一点思考
· 为什么说在企业级应用开发中,后端往往是效率杀手?
· DeepSeek 解答了困扰我五年的技术问题。时代确实变了!
· 本地部署DeepSeek后,没有好看的交互界面怎么行!
· 趁着过年的时候手搓了一个低代码框架
· 推荐一个DeepSeek 大模型的免费 API 项目!兼容OpenAI接口!