BZOJ1827 奶牛大集会 - 树形dp

17 篇文章 0 订阅

传送门

题解:分两遍dfs来做,首先求出f[x]表示以x为根且为重心,子树到其加权距离和。

转移即f[x]=\sigma {f[y]+sz[y]*dist(x,y)},其中y是x的子节点,sz为子树大小。

然后利用f数组计算答案,发现答案比f数组多了从父亲来的部分,用v表示

则v[x]=v[y]+(full_size-sz[x])*dist(x,y),其中y为x的父节点,full_size为整棵树的大小(也就是n)。

最后v[x]+f[x]就是以x为重心的代价。取最小值即可。

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
#include<climits>
#define INF LLONG_MAX
#define lint long long
#define MAXN 100010
using namespace std;
struct node{
    int to,wgt;
    node(int _t,int _w)
    {
        this->to=_t;
        this->wgt=_w;
    }
};
vector<node> g[MAXN];lint a[MAXN];
lint minans,f[MAXN],sz[MAXN],fsz;
//lint val[MAXN];
int getf(int x,int fa)
{
    for(int i=g[x].size()-1;i>=0;i--)
        if(g[x][i].to!=fa)
        {
            getf(g[x][i].to,x);
            sz[x]+=sz[g[x][i].to];
            f[x]+=sz[g[x][i].to]*g[x][i].wgt+f[g[x][i].to];
        }
    return 0;
}
int getans(int x,int fa,lint v)
{
//  val[x]=v;
    minans=min(minans,f[x]+v);
    for(int i=g[x].size()-1;i>=0;i--)
        if(g[x][i].to!=fa)
            getans(g[x][i].to,x,v+f[x]-sz[g[x][i].to]*g[x][i].wgt-f[g[x][i].to]+(fsz-sz[g[x][i].to])*g[x][i].wgt);
    return 0;
}
int main()
{
    int n;scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%lld",&sz[i]);
    for(int i=1;i<=n;i++) a[i]=sz[i];
    for(int i=1;i<n;i++)
    {
        int u,v,w;scanf("%d%d%d",&u,&v,&w);
        g[u].push_back(node(v,w));
        g[v].push_back(node(u,w));
    }
    minans=INF;getf(1,0);
//  for(int i=1;i<=n;i++)
//      printf("%lld ",f[i]);
//  printf("\n");
    fsz=sz[1];getans(1,0,0LL);
//  for(int i=1;i<=n;i++)
//      printf("%lld ",val[i]);
//  printf("\n");
    printf("%lld\n",minans);return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值