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;
}

浙公网安备 33010602011771号