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