1. 程式人生 > 實用技巧 >CF1436D Bandit in a City

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;
}