CF1436D

我们来看一下此题:

看到最大值最小,很容易想到二分答案,设二分的值为 $x$

再设 $sum_i$ 是 $i$ 的子树中 $a_j$ 的和,$l_i$ 表示 $i$ 的子树中叶子结点的个数,

那么我们假设在点 $u$ 上,且 $u$ 的子树除了 $u$ 以外全部分配完成,那么 $u$ 上的人肯定会优先去人比较少的叶子节点,

那么如果目前合法,$u$ 不合法当且仅当 $x < \left\lceil\dfrac{sum_{u}}{l_u}\right\rceil$,

时间复杂度:$O(n log n)$

其实可以不用二分,直接取 $\left\lceil\dfrac{sum_{u}}{l_u}\right\rceil$ 的最大值即可

时间复杂度:$O(n)$


#include <cmath>
#include <cstdio>
#include <iostream>
using namespace std;
#define int long long // 别忘开 long long

const int maxn = 2e5 + 10;
struct node {
    int to, nxt;
} edge[maxn << 1];
int cnt, head[maxn];
int ans = 0, a[maxn], l[maxn], sum[maxn];

void add (int u, int v) { // 建边
    cnt ++;
    edge[cnt].to = v;
    edge[cnt].nxt = head[u];
    head[u] = cnt;
}

void dfs (int u, int f) {
    sum[u] = a[u];
    int son = 0;
    for (int i = head[u]; i; i = edge[i].nxt) {
        int v = edge[i].to;
        if (v == f) continue;
        dfs (v, u);
        son ++;
        l[u] += l[v]; sum[u] += sum[v];
    }
    if (son == 0) l[u] = 1;
    int res = sum[u] / l[u];
    if (res * l[u] < sum[u]) res ++; 
    ans = max (ans, res); // 取最大值
}

signed main() {
    int n; scanf ("%lld", &n);
    for (int i = 2; i <= n; ++i) {
        int x; scanf ("%lld", &x);
        add (x, i); add (i, x);
    }
    for (int i = 1; i <= n; ++i) scanf ("%lld", &a[i]);
    dfs (1, 0);
    printf ("%lld", ans);
    return 0;
}
posted @ 2021-10-14 22:56  wangzhongyuan  阅读(2)  评论(0编辑  收藏  举报  来源