CSP-S 2023 种树
二分,贪心
显然如果 \(t\) 天能完成,则 \(> t\) 天也一定可以。所以二分枚举天数 \(t\)。考虑 check 函数,如果我们能够求出第 \(i\) 块区域最晚需要哪天种树,然后贪心判断即可。此时有两个问题:
- 求第 \(i\) 块区域最晚需要哪天种树。
预处理出 \(d_i\) 表示第 \(i\) 块区域从哪天开始,每天只会增长 \(1\)。如果 \(> 10^9\),赋 \(\infty\) 后跳过;否则二分查找即可。显然满足单调性。同样,我们也二分查找 \(i\) 块区域最晚需要哪天种树。对于一段时间 \([l, r]\),树会长高
\[\Delta{i} = \begin{cases}
r - l + 1, & l \geq d_i \\
(r - l + 1) \times b_i + \dfrac{(l + r) \times (r - l + 1)}{2} \times c_i, & r < d_i \\
(d_i - l) \times b_i + \dfrac{(l + d_i - 1) \times (d_i - l)}{2} \times c_i + (r - d_i + 1), & l < d_i \leq r
\end{cases}
\]
问题也就是求使得 \(\Delta{i} \geq a_i\) 的最小 \(l\),其中 \(r\) 固定(是我们最开始二分得到的 \(t\))。
- 判断是否可以在 \(t\) 天内完成。
我们求出了第 \(i\) 块区域最晚需要哪天种树,我们肯定会希望这天最早的先种,于是我们每次找到这块区域 \(u\) 种树,然后从 \(u\) 开始往上跳,直到遇到已经种过的区域停止,那么这中间路径上的区域都应该种上树。如果出现无法种植的,则不满足要求。
时间复杂度为 \(O(n \log^2 w)\)。
#include <bits/stdc++.h>
using i64 = long long;
using i128 = __int128;
using pii = std::pair<int, int>;
const int N = 1e5 + 5;
int n;
i64 a[N];
int b[N], c[N];
int d[N], f[N];
std::vector<int> g[N];
int tmp[N];
bool vis[N];
bool grow(int l, int r, int x) {
if (l >= d[x]) {
return r - l + 1 >= a[x];
}
i128 res = 0;
if (r >= d[x]) {
res = r - d[x] + 1;
r = d[x] - 1;
}
int len = r - l + 1;
res += (i128) len * b[x] + (i128) (l + r) * len / 2 * c[x];
return res >= a[x];
}
void dfs(int u, int father) {
f[u] = father;
for (auto v : g[u]) {
if (v == father) {
continue;
}
dfs(v, u);
}
}
bool check(int x) {
memset(tmp, 0, sizeof(tmp));
memset(vis, 0, sizeof(vis));
std::priority_queue<pii, std::vector<pii>, std::greater<pii>> q;
for (int i = 1; i <= n; i++) {
int l = 1, r = x;
while (l <= r) {
int mid = (l + r) >> 1;
if (grow(mid, x, i)) {
tmp[i] = mid;
l = mid + 1;
} else {
r = mid - 1;
}
}
if (!tmp[i]) {
return false;
}
if (i > 1) {
q.push({tmp[i], i});
}
}
int now = 1;
vis[1] = true;
while (!q.empty()) {
int u = q.top().second;
q.pop();
if (vis[u]) {
continue;
}
vis[u] = true;
int len = 1, v = f[u];
while (!vis[v]) {
len++;
vis[v] = true;
v = f[v];
}
int res = len;
while (len > 0) {
if (now + len > tmp[u]) {
return false;
}
u = f[u];
len--;
}
now += res;
}
return true;
}
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cin >> n;
for (int i = 1; i <= n; i++) {
std::cin >> a[i] >> b[i] >> c[i];
}
for (int i = 1; i < n; i++) {
int u, v;
std::cin >> u >> v;
g[u].push_back(v);
g[v].push_back(u);
}
for (int i = 1; i <= n; i++) {
d[i] = 2e9;
int l = 1, r = 1e9;
while (l <= r) {
int mid = (l + r) >> 1;
if (1ll * b[i] + 1ll * mid * c[i] < 1) {
d[i] = mid;
r = mid - 1;
} else {
l = mid + 1;
}
}
}
dfs(1, 0);
int l = n, r = 1e9, ans;
while (l <= r) {
int mid = (l + r) >> 1;
if (check(mid)) {
ans = mid;
r = mid - 1;
} else {
l = mid + 1;
}
}
std::cout << ans << '\n';
return 0;
}

浙公网安备 33010602011771号