BZOJ 1827 洛谷 2986 [USACO10MAR]伟大的奶牛聚集Great Cow Gather-LMLPHP

【题解】

  很容易想到暴力做法,枚举每个点,然后对于每个点O(N)遍历整棵树计算答案。这样整个效率是O(N^2)的,显然不行。

  我们考虑如果已知当前某个点的答案,如何快速计算它的儿子的答案。

  显然选择它的儿子作为集合点,它的儿子的子树内的奶牛可以少走当前点到儿子节点的距离dis,不在它儿子的子树内的奶牛要多走dis. 那么我们维护每个节点的子树内的奶牛总数(即点权和),就可以快速进行计算了。效率O(N).

  

 #include<cstdio>
#include<algorithm>
#define N 200010
#define rg register
#define LL long long
using namespace std;
int n,tot,last[N],v[N];
LL ans,sum,size[N],dis[N];
struct edge{
int to,pre,dis;
}e[N];
inline int read(){
int k=,f=; char c=getchar();
while(c<''||c>'')c=='-'&&(f=-),c=getchar();
while(''<=c&&c<='')k=k*+c-'',c=getchar();
return k*f;
}
void dfs(int x,int fa){
size[x]=v[x];
for(rg int i=last[x],to;i;i=e[i].pre)if((to=e[i].to)!=fa){
dis[to]=dis[x]+e[i].dis;
dfs(to,x); size[x]+=size[to];
}
}
void solve(int x,int fa,LL now){
ans=min(ans,now);
for(rg int i=last[x],to;i;i=e[i].pre)if((to=e[i].to)!=fa){
LL tmp=now-size[to]*e[i].dis+(sum-size[to])*e[i].dis;
solve(to,x,tmp);
}
}
int main(){
n=read();
for(rg int i=;i<=n;i++) v[i]=read(),sum+=v[i];
for(rg int i=;i<n;i++){
int u=read(),v=read(),w=read();
e[++tot]=(edge){v,last[u],w}; last[u]=tot;
e[++tot]=(edge){u,last[v],w}; last[v]=tot;
}
dfs(,);
for(rg int i=;i<=n;i++) ans+=dis[i]*v[i];
solve(,,ans);
printf("%lld\n",ans);
return ;
}
05-11 13:08