GDCPC

C dfs序

题目:

思路:这是一道推公式的贪心题,不难发现,一个根节点子树的遍历顺序与子树的大小与权值之和有关,显而易见的应该用这两个关键字推导公式。首先关于pi * wi,这里直接求出每个点的遍历次序显然是不可能的,这里可以应用递推的思想把这个遍历次序转换,

例如一颗如图所示的树

这里先不考虑权值,只考虑如何将pi * wi转化,令w为子树的点权之和,_size为子树大小,均不包含根节点

最开始以1为根节点的子树先遍历2节点,此时2节点的带来的贡献w[2] * 1, 3节点带来的贡献就是w[3] * (_size[2] +1)。注意到3是整棵树最后遍历的,并且_size[2] + 1为4,符合遍历顺序(因为这里没考虑根节点,因此所有点的遍历顺序都比实际少1,只需要在最后求解时加上w[1]修正即可)此时接着遍历2节点,只能遍历到4,4(子树)的贡献为w[4] * 1,此时4这个点的权值加了两遍符合顺序。。。以此类推。为什么要转化这个pi * wi,因为只有转化后方便我们做递归操作

接着开始寻找遍历顺序,顺着上面的思想推个公式假设节点x有n颗子树,为了简化问题,这里n取2(其实是不会证n...一堆式子看着就头疼)分别为1  2设先计算1为最优解 则有 w[1] * 1 + w[2] * (_size[1] + 1) > w[2] * 1 + w[1] * (_size[2] + 1) 化简 w[2] * _size[1] > w[1] * _size[2]递推式子就出来了...只要在dfs时,对于每个u临边遍历完之后对其临边按照这个式子sort即可

代码:

#include <bits/stdc++.h>

using namespace std;

typedef long long LL;

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int n;
    cin >> n;
    vector<LL> a(n + 1);
    vector<vector<int>> adj((n + 1));
    for(int i = 1; i <= n; i ++ ) cin >> a[i];
    for(int i = 2; i <= n; i ++ ) {
        int x;
        cin >> x;
        adj[x].push_back(i);
    }
    
    vector<LL> _size(n + 1);
    vector<LL> w(n + 1);
    function<void(int)> dfs = [&](int u) -> void {
        _size[u] = 1;
        w[u] = a[u];
        for(auto t: adj[u]) {
            dfs(t);
            _size[u] += _size[t];
            w[u] += w[t];
        }
        sort(adj[u].begin(), adj[u].end(), [&](int a, int b) -> bool {
            return w[a] * _size[b] < _size[a] * w[b];
        });
    };
    dfs(1);
    
    LL ans = 0;
    function<void(int)> dfss = [&](int u) -> void {
        int sum = 0;
        for(auto t: adj[u]) {
            dfss(t);
            ans += w[t] * (sum + 1);
            sum += _size[t];
        }
    };
    dfss(1);
    cout << ans + w[1];
    return 0;
}

  • 9
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值