P9755 [CSP-S 2023] 种树 题解
Description
你是一个森林养护员,有一天,你接到了一个任务:在一片森林内的地块上种树,并养护至树木长到指定的高度。
森林的地图有
你的目标是:在每片地块上均种植一棵树木,并使得
你每天可以选择一个未种树且与某个已种树的地块直接邻接(即通过单条道路相连)的地块,种一棵高度为
对每个地块而言,从该地块被种下树的当天开始,该地块上的树每天都会生长一定的高度。由于气候和土壤条件不同,在第
你想知道:最少需要多少天能够完成你的任务?
Solution
考虑二分答案。
容易发现那个相邻块的限制就是每个点选的时间不早于它的所有祖先。
设
然后思考怎样 check。
有一个想法是把
但是这样做不能保证子孙在祖先之后。
容易发现
这样再 sort 一遍就一定会满足原题相邻块的限制条件了。
时间复杂度:
Code
#include <bits/stdc++.h> #define int int64_t const int kMaxN = 1e5 + 5; int n; int a[kMaxN], b[kMaxN], c[kMaxN], t[kMaxN], buc[kMaxN]; std::vector<int> G[kMaxN]; __int128 getval(int t, int b, int c) { int lim; if (c >= 0) lim = t; else lim = std::min(t, (b - 1) / (-c)); return b * lim + (__int128)lim * (lim + 1) / 2 * c + t - lim; } bool chk(int l, int r, int a, int b, int c) { return getval(r, b, c) - getval(l - 1, b, c) >= a; } void dfs(int u, int fa) { for (auto v : G[u]) { if (v == fa) continue; dfs(v, u); t[u] = std::min(t[u], t[v] - 1); } } bool check(int x) { static int tt[kMaxN]; for (int i = 1; i <= n; ++i) { int L = 0, R = x + 1, res = -1e9; if (!chk(1, x, a[i], b[i], c[i])) return 0; __int128 tot = getval(x, b[i], c[i]); while (L + 1 < R) { int mid = (L + R) >> 1; if (tot - getval(mid - 1, b[i], c[i]) >= a[i]) L = res = mid; else R = mid; } t[i] = res; } dfs(1, 0); int m1 = 0, m2 = n; for (int i = 1; i <= n; ++i) { if (t[i] <= 0) { return 0; } } for (int i = 1; i <= n; ++i) { if (t[i] > n) tt[m2--] = t[i]; else ++buc[t[i]]; } for (int i = 1; i <= n; ++i) for (; buc[i]; --buc[i]) tt[++m1] = i; for (int i = 1; i <= n; ++i) t[i] = tt[i]; // std::sort(t + 1, t + 1 + n); for (int i = 1; i <= n; ++i) if (t[i] < i) return 0; return 1; } void dickdreamer() { 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].emplace_back(v), G[v].emplace_back(u); } int L = 0, R = 1e9 + 1, res = 1e9; while (L + 1 < R) { int mid = (L + R) >> 1; if (check(mid)) R = res = mid; else L = mid; } std::cout << res << '\n'; } int32_t main() { #ifdef ORZXKR freopen("in.txt", "r", stdin); freopen("out.txt", "w", stdout); #endif std::ios::sync_with_stdio(0), std::cin.tie(0), std::cout.tie(0); int T = 1; // std::cin >> T; while (T--) dickdreamer(); // std::cerr << 1.0 * clock() / CLOCKS_PER_SEC << "s\n"; return 0; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步