题解:P9755 [CSP-S 2023] 种树
第 \(x\) 天在地块 \(u\) 种下树后,那棵树在当天就会长 \(\max(b_u + x \times c_u, 1)\) 米,到完成任务的第 \(T\) 天,一共会长 \(\sum_{i=x}^T \max(b_u + i \times c_u, 1)\) 米。
可见每天至少长高 \(1\) 米,因此一定存在最优方案在 \([1, n]\) 天每天都种一棵树,再等他们都长到对应高度。
因此如果能在 \(t\) 天完成任务,那么 \(> t\) 天一定也能完成任务,树不可能缩水。因此考虑二分 \(t\)。
接下来要解决一个可行性判定:能否安排一种种树的顺序,使得以 \(1\) 为根时,父亲一定要比儿子先种,且第 \(t\) 天,对于每个 \(u\),地块 \(u\) 上的树的高度 \(\ge a_u\)。
既然问到顺序了,那么 \(n!\) 枚举所有可能的顺序,再检查是否合法,总是一个方法。但实际上所有合法的顺序就是树上的拓扑序,枚举所有拓扑序说不定能过所有满足 \(n \leq 20\) 的测试点。
任务安排类问题可以考虑每个任务的截止时间。本题中,地块 \(u\) 的截止时间即最大的 \(x\) 使得 \(\sum_{i=x}^T \max(b_u + i \times c_u, 1) \ge a_u\),因为 \(x\) 越大该式子越小。
设对于地块 \(u\) 上的树来说,这样的 \(x\) 为 \(d_u\)。因为单调性,\(d_u\) 也是可以使用二分求解的。计算该式子也是简单的,因为由最多两段等差数列组成。
实际上可以把 \(d_u\) 对 \(n\) 取 \(\min\)。因为“一定存在最优方案在 \([1, n]\) 天每天都种一棵树“。这一步并不必要,只是为了讨论方便。
问题现在转换成了,是否存在一个树上的拓扑序,使得结点 \(u\) 在拓扑序列中的下标 \(\leq d_u\)。
因为是拓扑序,所以父亲只能出现在每个儿子之前。就算有一条边 \((u, v)\),满足 \(u\) 是 \(v\) 的父亲,且 \(d_u > d_v\),\(u\) 在拓扑序列也不能比 \(v\) 来得更后面。
因此以后序遍历的顺序,对所有 \(u\) 做 \(d_u \gets \min(d_u, d_v - 1)\) 即可。这样既是必要的也是充分的,所以拓扑序的限制就被干掉了。
现在转换成的问题十分简洁:是否存在一个 \(\{1, 2, \dots, n\}\) 的排列 \(p\),使得对于每个 \(i\),有 \(p_i \leq d_i\)。
这可以看作一个二分图完美匹配判定问题。左部点是结点 \(\{1, 2, \dots, n\}\),右部点是下标 \(\{1, 2, \dots, n\}\),左部点 \(u\) 与所有满足 \(i \leq d_u\) 的右部点 \(i\) 连一条无向边,问完美匹配是否存在。
Hall 定理指出,存在完美匹配当且仅当对于每个 \(i\),\(\# \{u \mid d_u \leq i \} \leq i\)。
至此,二分的判定完成,整道题的思路便也完成。
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using lll = __int128_t;
constexpr int N = 100000 + 1;
ll a[N];
int b[N], c[N], cnt[N], ord[N], d[N];
vector<int> adj[N];
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
cin >> n;
for (int i = 1; i <= n; ++i) {
cin >> a[i] >> b[i] >> c[i];
}
for (int i = 1; i < n; ++i) {
int u, v;
cin >> u >> v;
adj[u].push_back(v);
adj[v].push_back(u);
}
int idx = 0;
auto dfs = [&](auto &&self, int u) -> void {
for (int v : adj[u]) {
erase(adj[v], u);
self(self, v);
}
ord[++idx] = u;
};
dfs(dfs, 1);
auto chk = [&](int t) {
for (int i = 1; i <= n; ++i) {
cnt[i] = 0;
auto calc = [&](int mid) -> lll {
if (c[i] >= 0) {
return (b[i] + c[i] * (ll)mid + b[i] + c[i] * (ll)t) * (lll)(t - mid + 1) >> 1;
}
int ti = (b[i] - 1) / -c[i];
if (ti < mid) {
return t - mid + 1;
}
ti = min(ti, t);
return ((b[i] + c[i] * (ll)mid + b[i] + c[i] * (ll)ti) * (lll)(ti - mid + 1) >> 1) + t - ti;
};
int l = 1, r = n;
while (l <= r) {
int mid = (l + r) >> 1;
if (calc(mid) >= a[i]) {
l = mid + 1;
} else {
r = mid - 1;
}
}
d[i] = r;
}
for (int i = 1; i <= n; ++i) {
int u = ord[i];
for (int v : adj[u]) {
d[u] = min(d[u], d[v] - 1);
}
if (d[u] <= 0) {
return false;
}
++cnt[d[u]];
}
int sm = 0;
for (int i = 1; i <= n; ++i) {
if ((sm += cnt[i]) > i) {
return false;
}
}
return true;
};
int l = 1, r = 1e9;
while (l <= r) {
int mid = (l + r) >> 1;
if (chk(mid)) {
r = mid - 1;
} else {
l = mid + 1;
}
}
cout << l << '\n';
return 0;
}
posted on 2026-06-26 20:53 SkyWave2022 阅读(13) 评论(0) 收藏 举报
浙公网安备 33010602011771号