Loading

CodeForces - 1473D Program 前缀和,最大子段和

CodeForces - 1473D Program 前缀和,最大子段和

题意

给定一段“+-”序列,\(+\)表示\(+1\),否则表示\(-1\)

现有\(l ,r\)表示无视\([l,r]\)剩下的序列能够得到多少种不同的数字

\[1\leq n,m \leq 2\times 10^5\\ 1 \leq l,r \leq n \]

分析

显然答案 = 从左到右 序列过程中的最大值减去最小值

去除\([l,r]\)后将原序列分成两部分,左边的最值显然可以用前缀和维护。

现在需要想一个办法来维护后缀的前缀,显然不能直接维护后缀的,于是用类似最大子段和的方法来维护后缀

代码

char s[maxn];
int pre[maxn];
int pre_mx[maxn],pre_mi[maxn],suf_mx[maxn],suf_mi[maxn];

void solve(){
	int n = rd();
	int m = rd();
	scanf("%s",s);
	for(int i = 1;i <= n;i++){
		pre[i] = pre[i - 1] + (s[i - 1] == '+' ? 1 : -1);
		pre_mx[i] = max(pre[i],pre_mx[i - 1]);
		pre_mi[i] = min(pre[i],pre_mi[i - 1]);
	}
	suf_mx[n + 1] = suf_mi[n + 1] = 0;
	for(int i = n;i >= 1;i--){
		suf_mx[i] = max(0,suf_mx[i + 1] + (s[i - 1] == '+' ? 1 : -1));
		suf_mi[i] = min(0,suf_mi[i + 1] + (s[i - 1] == '+' ? 1 : -1));
	}
	while(m--){
		int l = rd();
		int r = rd();
		l--;
		int mx,mi;
		mx = mi = 0;
		mx = max(mx,pre_mx[l]);
		mi = min(mi,pre_mi[l]);
		int tmp = pre[l];
		mx = max(mx,suf_mx[r + 1] + tmp);
		mi = min(mi,suf_mi[r + 1] + tmp);
		cout << mx - mi + 1 << '\n';
	} 
}

int main(){
	int T = rd();
	while(T--)
		solve();
}
posted @ 2021-02-05 10:39  MQFLLY  阅读(129)  评论(0编辑  收藏  举报