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;
}
posted @ 2024-10-03 15:56  Unino  阅读(28)  评论(0)    收藏  举报