CF1436D Bandit in a City
阿新 • • 發佈:2020-11-13
場上亂搞了個假 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\)
但一般無法達到理想狀態,設 \(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\)
對於所有葉節點 \(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;
}