[USACO10MAR]伟大的奶牛聚集

首先要想到\(dp\)转移方程,设\(g[v]\)为设\(v\)为牛棚的花费,可以由其父亲转移而来
\[g[v]=g[u]-sz[v]*w+(tot-sz[v])*w\]
之后我们随便制定一个树根算出初态(比如设定\(1\)为树根初态)即可开始转移

#include <cstdio>
#include <algorithm>
using namespace std;
#define MAXN 100010
#define ll long long
ll f[MAXN];
ll g[MAXN];
int head[MAXN],nxt[MAXN*2],vv[MAXN*2],ww[MAXN*2],tot;
inline void add_edge(int u, int v, int w){
    vv[++tot]=v;
    ww[tot]=w;
    nxt[tot]=head[u];
    head[u]=tot;
}
int sz[MAXN];
int c[MAXN];
void dfs(int u, int fa){
    sz[u]=c[u];
    for(int i=head[u];i;i=nxt[i]){
        int v=vv[i],w=ww[i];
        if(v==fa) continue;
        dfs(v, u);
        sz[u]+=sz[v];
        f[u]+=f[v]+(ll)sz[v]*w;
    }
}
const ll INF=1e18;
ll ans=INF;
void dfs2(int u, int fa){
    for(int i=head[u];i;i=nxt[i]){
        int v=vv[i],w=ww[i];
        if(v==fa) continue;
        g[v]=g[u]+(ll)w*(sz[1]-sz[v])-(ll)sz[v]*w;
        ans=min(ans, g[v]);
        dfs2(v, u);
    }
}
int n;
int main(){
    scanf("%d", &n);
    for(int i=1;i<=n;++i) scanf("%d", &c[i]);
    for(int i=1;i<n;++i){
        int u,v,w;scanf("%d %d %d", &u, &v, &w);
        add_edge(u, v, w);
        add_edge(v, u, w);
    }
    dfs(1,1);
    g[1]=f[1];
    ans=min(ans, g[1]);
    dfs2(1, 1);
    printf("%lld\n", ans);
    return 0;
}
/*
 f[u]+=f[v]+sz[v]*w
 g[v]=g[u]+w*(cnt-sz[v])-sz[v]*w
 */
01-08 13:23