洛谷 P2986 [USACO10MAR] Great Cow Gathering G(树形dp)

洛谷 P 2986   [ U S A C O 10 M A R ]   G r e a t   C o w   G a t h e r i n g   G \Huge{洛谷P2986\ [USACO10MAR]\ Great\ Cow\ Gathering\ G} 洛谷P2986 [USACO10MAR] Great Cow Gathering G

文章目录


本题有一道简单版本,难度属于树形dp的简单题。

题目地址:P3478 [POI2008] STA-Station - 洛谷

题解:[POI2008] STA-Station/洛谷P3478(树形dp)-CSDN博客

题意

n n n个农场,每个农场相互可以到达(联通),有 n − 1 n-1 n1条路连接这 n n n个农场。每条路对应的有其长度 L i L_i Li,然后每个农场有若干头牛 C i C_i Ci

现在要选择一个农场,并让所有牛都去,求再哪个农场举办能使得所有牛移动距离最小?输出最小的移动距离总和。

数据范围:

  • 1 ≤ n ≤ 1 0 5 1\leq n\leq 10^5 1n105
  • 1 ≤ A i ≤ B i ≤ n 1\leq A_i\leq B_i\leq n 1AiBin
  • 0 ≤ C i , L i ≤ 1 0 3 0 \leq C_i,L_i \leq 10^3 0Ci,Li103

思路

对于这道题,如果写过上面那道简单题的话,这道题很容易就能写出来。不过这一道也属于树形 d p dp dp中的典中典。

这两道题都属于树形 d p dp dp中的选择节点型。

对于这道题,假设x节点最优,那么x节点的最小移动距离总和=所有子节点的移动距离总和+每个子节点的子节点奶牛个数*这个子节点距离x的距离。

上面的描述可以用下列公式表示为:
b [ x ] . s e c o n d = ∑ b [ i ] . s e c o n d + b [ i ] . f i r s t × z b[x].second=\sum{b[i].second+b[i].first\times z} b[x].second=b[i].second+b[i].first×z
以上涉及到的每个节点的子孙节点个数以及移动距离总和,都可以通过一次 d f s dfs dfs完成。

然后考虑从 x x x节点转移到其子节点 i i i的状态转移方程:
b [ i ] . s e c o n d = b [ x ] . s e c o n d + ( ( b [ 1 ] . f i r s t − b [ i ] . f i r s t ) × z ) − b [ i ] . f i r s t × z b[i].second = b[x].second + ((b[1].first - b[i].first) \times z) - b[i].first \times z b[i].second=b[x].second+((b[1].firstb[i].first)×z)b[i].first×z
第二个dfs中 b [ 1 ] . f i r s t b[1].first b[1].first相当于所有奶牛的个数。

标程

#define int long long 
#define fi first 
#define se second
const int N = 1e5 + 10;

int n, res;
vector<PII> a[N];
vector<PII> b(N);

void dfs1(int x, int y) {
    for(auto [i, z] : a[x]) {
        if(i == y) continue;
        dfs1(i, x);
        b[x].fi += b[i].fi;
        b[x].se += b[i].fi * z + b[i].se;
    }
}

void dfs2(int x, int y) {
    for(auto [i, z] : a[x]) {
        if(i == y) continue;
        b[i].se = b[x].se + ((b[1].fi - b[i].fi) * z) - b[i].fi * z;
        dfs2(i, x);
    }
}

void Solved() {
    cin >> n;
    for(int i = 1; i <= n; i ++ ) {
        cin >> b[i].fi;
    }
    for(int i = 1; i < n; i ++ ) {
        int x, y, z; cin >> x >> y >> z;
        a[x].push_back({y, z}); a[y].push_back({x, z});
    }
    dfs1(1, 1);
    dfs2(1, 1);

    res = 1e18;
    for(int i = 1; i <= n; i ++ ) {
        res = min(res, b[i].se);
    }
    cout << res << endl;
}
  • 31
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值