场上乱搞了个假 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;
}