【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\) 取较大值

因为可以什么也不加。

然后最大值和最小值会在第一段或第二段中出现,第一段中出现的话,对应 preSumMaxpreSumMin, 而在第二段中

出现的话,对应的就是 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;
}
posted @ 2021-02-27 15:07  AWCXV  阅读(39)  评论(0编辑  收藏  举报