CF1556E

传送门:https://www.luogu.com.cn/problem/CF1556E

做一个等价的转换,令 \(c_i=a_i-b_i\),我们每次操作可以对 \(c\) 序列进行 \(+1,-1,+1,-1..+1,-1\)

首先考虑第一个数,如果第一个数是正数显然无解,如果是一个负数则需要至少对应绝对值次操作。再考虑第二个数,如果是负数则可以和第一个数合并依次操作。至少需要 \(-c_1-c_2\) 次操作。

如果是正数呢?我们可以将这个正数 \(-1\) 作为桥梁同时进行下一段 \(+1\)。如果下一段有 \(k\)\(-1\),那么后 \(k\)\(+1\) 就是免费的。当然如果前面的负数不足 \(k\) 个,那么就无解了。

具体实现可以实时维护以 \(l\) 为左端点的一段和(始终是负数表示未被抵消的负值量),如果某个位置变为正数则直接判断不合法,如果是下一个数是负值直接累加操作次数后直接与最大操作数取 \(\max\),如果是正数那么先与最大操作次数取 \(\max\),然后将前缀值加上这个正数来抵消后 \(k\) 次负数。综上,我们发现维护的这段和只是简单的以 \(l\) 为端点的前缀和,答案是所有这些前缀和中的最大值。

我们发现问题和判断一段括号子序列是否合法异常的相似,把 \(+1\) 看作左括号,\(-1\) 看作右括号,合法的充要条件是括号序列合法。答案为栈中每个时刻残留的左括号的最大值。用ST表维护前缀和序列可以轻松实现。

空间复杂度 \(O(n)\),时间复杂度 \(O(n\log n+q)\)

#include <bits/stdc++.h>

#define int long long

const int N = 1e5 + 1;

using namespace std;

int a[N], b[N], n;

int Max[N][17], lg[N], Min[N][17];

void work() {
    for (int i = 1; i <= n; ++i) Max[i][0] = Min[i][0] = a[i];
    lg[2] = 1;
    for (int i = 3; i <= n; ++i) lg[i] = lg[i / 2] + 1;
    for (int j = 1; j <= 16; ++j) {
        for (int i = 1; i + (1 << j) - 1 <= n; ++i) {
            Max[i][j] = max(Max[i][j - 1], Max[i + (1 << j - 1)][j - 1]);
            Min[i][j] = min(Min[i][j - 1], Min[i + (1 << j - 1)][j - 1]);
        }
    }
}

int queryMax(int l, int r) {
    int s = lg[r - l + 1];
    return max(Max[l][s], Max[r - (1 << s) + 1][s]);
}

int queryMin(int l, int r) {
    int s = lg[r - l + 1];
    return min(Min[l][s], Min[r - (1 << s) + 1][s]);
}


signed main() {
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    int q;
    cin >> n >> q;
    for (int i = 1; i <= n; ++i) cin >> a[i];
    for (int i = 1; i <= n; ++i) cin >> b[i];
    for (int i = 1; i <= n; ++i) a[i] -= b[i], a[i] += a[i - 1];
    work();
    while (q--) {
        int l, r;
        cin >> l >> r;
        if (a[r] - a[l - 1] || queryMax(l, r) - a[l - 1]) cout << -1 << '\n';
        else cout << -queryMin(l, r) + a[l - 1] << '\n';
    }
    return 0;
}
posted @ 2026-05-11 00:55  Jefferyzzzz  阅读(8)  评论(0)    收藏  举报