CF1436D Bandit in a City

场上乱搞了个假 DP 吃了两发= =

简述

原题面:Codeforces

给定一棵 \(n\) 个节点的有根树,根为 \(1\),第 \(i\) 个节点上有 \(a_i\) 个人。
每个人可以往任意子节点走,直到走到叶节点,求最后人最多的叶节点的最少人数。
\(2\le n\le 2\times 10^5\)\(0\le a_i\le 10^9\)
1S,256MB

分析

知识点:树形 DP

\(\operatorname{sum}_{u}\) 表示 \(u\) 子树中所有节点的人数之和,\(\operatorname{leaf}_u\) 表示 \(u\) 子树中叶节点的个数。
首先考虑最理想状态,对于节点 \(u\),若它子树中的所有人都能均匀地散布在所有叶节点中,则显然该子树中 人最多的叶节点的人数为 \(\left\lceil \frac{\operatorname{sum}_u}{\operatorname{leaf}_u} \right\rceil\)

但一般无法达到理想状态,设 \(f_{u}\) 表示以 \(u\) 为根的子树中人最多的叶节点的人数。对于节点 \(u\) 的某儿子 \(v\),显然,存在 \(f_{v} > \left\lceil \frac{\operatorname{sum}_u}{\operatorname{leaf}_u} \right\rceil\) 时无法均分。
反之,当 \(\forall v\in son(u),\ f_v\le \left\lceil \frac{\operatorname{sum}_u}{\operatorname{leaf}_u} \right\rceil\) 时,可以将 \(a_u\) 按一定方案分配到各叶节点,形成均匀散布的形式。

对于所有叶节点 \(u\),初始化 \(\operatorname{leaf}_u = 1\),则有显然的状态转移方程:

\[\begin{aligned} \operatorname{sum}_u &= a_u + \sum_{v\in son(u)} \operatorname{sum}_v\\ \operatorname{leaf}_u &= \sum_{v\in son(u)} \operatorname{leaf}_v\\ f_{u} &= \max\left\{ \max_{v\in son(u)}\{f_v\},\ \left\lceil \frac{\operatorname{sum}_u}{\operatorname{leaf}_u} \right\rceil\right\} \end{aligned}\]

答案即为 \(f_1\)

算法总时间复杂度 \(O(n)\)

还有种被卡 ull 的暴力二分答案,可以参考其他题解。

实现

//知识点:树形DP 
/*
By:Luckyblock
*/
#include <algorithm>
#include <cctype>
#include <cstdio>
#include <cstring>
#include <vector>
#include <cmath>
#define LL long long
const int kN = 2e5 + 10;
//=============================================================
int n, e_num, a[kN], head[kN], v[kN], ne[kN];
LL leaf[kN], sum[kN], f[kN];
bool fa[kN];
//=============================================================
inline int read() {
  int f = 1, w = 0;
  char ch = getchar();
  for (; !isdigit(ch); ch = getchar())
    if (ch == '-') f = -1;
  for (; isdigit(ch); ch = getchar()) {
    w = (w << 3) + (w << 1) + (ch ^ '0');
  }
  return f * w;
}
void Chkmax(LL &fir_, LL sec_) {
  if (sec_ > fir_) fir_ = sec_;
}
void Chkmin(LL &fir_, LL sec_) {
  if (sec_ < fir_) fir_ = sec_;
}
void AddEdge(int u_, int v_) {
  v[++ e_num] = v_;
  ne[e_num] = head[u_];
  head[u_] = e_num;
}
void Dfs(int u_) {
  if (! fa[u_]) leaf[u_] = 1;
  sum[u_] = a[u_];
  
  for (int i = head[u_]; i; i = ne[i]) {
    int v_ = v[i];
    Dfs(v_);
    Chkmax(f[u_], f[v_]);
    sum[u_] += sum[v_];
    leaf[u_] += leaf[v_];
  }
  Chkmax(f[u_], ceil(1.0 * sum[u_] / leaf[u_]));
}
//=============================================================
int main() {
  n = read();
  for (int v_ = 2; v_ <= n; ++ v_) {
    int u_ = read();
    AddEdge(u_, v_);
    fa[u_] = true;
  }
  for (int i = 1; i <= n; ++ i) a[i] = read();
  Dfs(1);
  printf("%lld\n", f[1]);
  return 0;
}
posted @ 2020-11-13 11:26  Luckyblock  阅读(94)  评论(0编辑  收藏  举报