CF954G Castle Defense

CF954G Castle Defense

一共有 \(n\) 面墙,初始有 \(a_i\) 个弓箭手在第 \(i\) 面墙的位置上。一个在 \(i\) 位置的弓箭手可以保护 \(|i - j| \leq r\) 的所有墙 \(j\)

你现在可以增派 \(k\) 个弓箭手并且任意分配它们的位置。你需要最大化被数量最少的弓箭手保护的墙被弓箭手保护的数量。

\(n \leq 5 \times 10^5, 0 \leq r \leq n, 0 \leq k \leq 10^{18} ,0 \leq a_i \leq 10^9\)

二分答案,贪心,单调队列


可以二分答案,现在的问题变为判定每个位置至少分配 \(x\) 个弓箭手是否合法

有一个显然的做法是用树状数组维护,时间复杂度 \(O(n\log n\log k)\) ,无法通过本题

初始的弓箭手的贡献可以用前缀和计算。新增的弓箭手的贡献形如滑动窗口,用单调队列类似地维护即可

时间复杂度 \(O(n\log k)\)

代码

#include <bits/stdc++.h>
using namespace std;

#prag\
ma GCC optimize(3)
typedef long long ll;
const int maxn = 5e5 + 10;
int n, m;
ll k, sum[maxn];

inline bool check(ll mid) {
  static int Q[maxn];
  static ll val[maxn];
  int l = 1, r = 0;
  ll res = 0, delta = 0;
  for (int i = 1; i <= n; i++) {
    if (l <= r && i - Q[l] > m + m) delta -= val[l++];
    ll s = sum[min(n, i + m)] - sum[max(0, i - m - 1)] + delta;
    if (s >= mid) continue;
    ll tmp = mid - s;
    delta += tmp, res += tmp;
    if (res > k) return 0;
    Q[++r] = i, val[r] = tmp;
  }
  return 1;
}

int main() {
  scanf("%d %d %I64d", &n, &m, &k);
  for (int i = 1; i <= n; i++) {
    scanf("%I64d", sum + i), sum[i] += sum[i - 1];
  }
  ll l = 0, r = 1100000000000000000ll, mid;
  while (l < r) {
    check(mid = (l + r + 1) >> 1) ? l = mid : r = mid - 1;
  }
  printf("%I64d", r);
  return 0;
}

猜你喜欢

转载自www.cnblogs.com/Juanzhang/p/10908858.html