LOADING

加载过慢请开启缓存 浏览器默认开启

P9519 pay

2023/8/12 OI 题解

P9519 pay 题解。

1. 题解

一眼二分答案,然后考虑如何快速判断当前 \(k\) 满足题意。

首先把一个位置左右两边的贡献拆开,这样就不需要考虑绝对值了。

对于左边的贡献,我们从左到右扫一遍,维护一个队列和一个 \(\mathrm{sum}\)\(\mathrm{sum}\) 表示当前位置 \(i\) 左边的贡献,队列表示这些贡献的位置。

每次 \(i\) 向后移动,\(\mathrm{sum}\) 减去队列长度,并且弹出队列中 \(k - d \le 0\) 的元素。

右边的贡献同理,算完贡献后和 \(a_i\) 比较即可。

时间复杂度 \(O(n \log V)\)\(V\) 为值域。

2. Code

#include <cstring>
#include <iostream>
#include <queue>
using namespace std;
const int N = 1e6 + 5;
int n, m, a[N], b[N];
long long l, r = 1e10, c[N], sum, cnt;
bool vis[N];
queue<int> Q;
bool check(long long k) {
    memset(c, 0, sizeof(c));
    sum = cnt = 0, Q = {};
    for (int i = 1; i <= n; i++) {
        sum -= cnt;
        if (cnt && k - (i - Q.front()) == 0) Q.pop(), --cnt;
        if (vis[i]) Q.push(i), ++cnt, sum += k;
        c[i] += sum;
    }
    sum = cnt = 0, Q = {};
    for (int i = n; i >= 1; i--) {
        sum -= cnt;
        if (cnt && k - (Q.front() - i) == 0) Q.pop(), --cnt;
        c[i] += sum;
        if (vis[i]) Q.push(i), ++cnt, sum += k;
    }
    for (int i = 1; i <= n; i++)
        if (c[i] < a[i]) return false;
    return true;
}
int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cin >> n >> m;
    for (int i = 1; i <= n; i++) cin >> a[i];
    for (int i = 1; i <= m; i++) cin >> b[i], vis[b[i]] = true;
    while (l < r) {
        long long mid = (l + r) >> 1;
        if (check(mid)) r = mid;
        else l = mid + 1;
    }
    cout << l;
    return 0;
}