【Educational Codeforces Round 102 D】Program
题目链接
翻译
给你一个长度为 \(n\) 的序列,每个字符为加法或减法操作,这些操作按顺序执行,且初始的时候你的数字为 \(0\)。
现在给你 \(m\) 个询问,这些询问用区间 (l,r)
描述,表示从 \(l\) 开始到 \(r\) 结束这一段的操作被忽略了。
然后剩下的操作还是按顺序执行的话,问你最后在数字变化的过程中,会出现多少个不同的数字。
题解
首先不同的数字个数,就是在变换的过程中出现的最大值和最小值之间的数字的个数。
首先是 \(1..l-1\) 这一段,这一段的最大值和最小值很容易用前缀和求得。更新前缀和的时候,顺便记录一下当前位置之前
出现的最大、最小前缀和就可以了。
至于后缀,我们可以这样想,在一个后缀前面加上一个加法操作的结果是什么呢?显然会让后缀出现的最大值和最小值分别
都加上 \(1\),在后缀前面加上一个减法类似,会让最大值和最小值都减去 \(1\)。
那么就这样逆推一下后缀会出现的最小值和最大值即可,即 sufSumMax[i] = sufSumMax[i+1]+d[i]
,当然要和 \(0\) 取较大值
因为可以什么也不加。
然后最大值和最小值会在第一段或第二段中出现,第一段中出现的话,对应 preSumMax
和 preSumMin
, 而在第二段中
出现的话,对应的就是 preSum[l-1]+sufSumMax[r+1]
和 preSum[l-1]+sufSumMin[r+1]
了
代码
#include <bits/stdc++.h>
#define LL long long
using namespace std;
const int N = 2e5;
int T, n, m;
string s;
int main() {
#ifdef LOCAL_DEFINE
freopen("in.txt", "r", stdin);
#endif // LOCAL_DEFINE
cin >> T;
while (T--) {
cin >> n >> m;
cin >> s;
s = " " + s;
int cur = 0;
vector<int> preSL(1, 0),preSR(1,0),preS(1,0);
for (int i = 1; i <= n; i++) {
int d = -1;
if (s[i] == '+') {
d = 1;
}
preS.push_back(preS.back() + d);
preSL.push_back(min(preSL.back(), preS.back()));
preSR.push_back(max(preSR.back(), preS.back()));
}
vector<int> sufSL(1, 0), sufSR(1, 0);
for (int i = n; i >= 1; i--) {
int d = -1;
if (s[i] == '+') {
d = 1;
}
sufSL.push_back(min(sufSL.back() + d, 0));
sufSR.push_back(max(sufSR.back() + d, 0));
}
reverse(sufSL.begin(), sufSL.end());
reverse(sufSR.begin(), sufSR.end());
while (m--) {
int l, r;
cin >> l >> r;
int ma = preSR[l - 1], mi = preSL[l - 1];
ma = max(ma, preS[l - 1] + sufSR[r]);
mi = min(mi, preS[l - 1] + sufSL[r]);
cout << ma - mi + 1 << endl;
}
}
return 0;
}