ABC348E Minimize Sum of Distances
题目大意
给定一棵共 \(n\) 个节点的树, 第 \(i\) 个点的权重为 \(c_i\)。定义 \(f(x)\) 表示树上所有点到节点 \(x\) 的距离乘上权重,即 \(f(x)=\sum\limits_{i=1}^n(c_i\times dis(x, i))\)。求 \(\min\limits_{u=1}^nf(u)\)。
Solve
一眼换根。
考虑先以节点 \(1\) 为根,则 \(f(1)=\sum\limits_{i=1}^n(c_i\times d_i)\),(\(d_i\) 表示 \(i\) 的深度)。然后考虑换根求 \(f(u),u\in[2,n]\)。
如图,以 \(u=2\) 时为例。记 \(sum_u\) 为当以 \(1\) 为根时 \(u\) 的子树内的点的权值和。当根从 \(fa_2=1\) 转移至 \(2\) 时,对于在 \(2\) 的子树里的节点,他们的深度都减少了 \(1\),那么 \(\sum\limits_{i\in son_2}(c_i\times d_i)\) 就总共减少了 \(sum_u\);对于不在 \(2\) 的子树里的点,他们的深度都增加了 \(1\),共增加 \(sum_1-sum_u\)。整理一下并推广至所有 \(u\in [2,n]\),有: \(f(u)=f(fa_u)-sum_u+sum_1-sum_u\)。
Code
#include<bits/stdc++.h>
#pragma GCC optimize(1,2,3,"Ofast","inline")
using namespace std;
#define int long long
inline int read()
{
short f=1;
int x=0;
char c=getchar();
while(c<'0'||c>'9') {if(c=='-') f=-1;c=getchar();}
while(c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
return x*f;
}
int n,a,b,c[100010],f[100010],ans;
vector<int>e[100010];
void dfs(int u,int fa,int d)
{
f[1]+=d*c[u];
for(auto i:e[u])
if(i!=fa) dfs(i,u,-~d),c[u]+=c[i];
}
void dp(int u,int fa)
{
for(auto i:e[u])
if(i!=fa) f[i]=f[u]-c[i]+c[1]-c[i],dp(i,u);
}
signed main()
{
n=read();
for(int i=1;i<n;i=-~i)
a=read(),b=read(),
e[a].push_back(b),e[b].push_back(a);
for(int i=1;i<=n;i=-~i) c[i]=read();
dfs(1,0,0);ans=f[1];dp(1,0);
for(int i=1;i<=n;i=-~i) ans=min(ans,f[i]);
return printf("%lld",ans),0;
}
标签:Distances,limits,Minimize,题解,sum,fa,int,节点,Sum
From: https://www.cnblogs.com/sorato/p/18246653