题意:给定你一个长度为n的序列,一个变量x初始值为0,序列中的+代表x+1,-代表x-1,m个询问,每给定l和r,问你假如把[l,r]区间内的序列去掉,其他顺序不变,变量x一共会有多少个不同的值。
思路:操作只有加一和减一,代表我们值一定是在连续变化的,所以我们只要能得到,序列的中的最小值和最大值那么就可以知道他有多少个不同的值。维护一个前缀和序列sum。
对于l和r,我们求得[0,r]的最小值和最大值,[r+1,n]的最小值和最大值,但是因为中间的[l,r]区间被去掉那么相应地这一部分的贡献就会在后面的区间被减掉,所以[r+1,n]实际的最小值和最大值都应该是原值减去中间这一部分的值。
由此我们用树状数组维护区间最值log^2的复杂度就可以完成,而且由于这道题仅涉及到了前缀区间的最值和后缀的最值,我们其实可用两个数组就可以完成维护。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXN = 2e5 + 7;
const int INF = 0x3f3f3f3f;
int n,m;
char s[MAXN];
int a[MAXN],c_min[MAXN],c_max[MAXN],sum[MAXN];
int lowbit(int x){ return x & (-x); }
void update(int p){//对于每个位置 依次去枚举它包含的位置 log^2的复杂度
while(p <= n){
c_min[p] = sum[p];
c_max[p] = sum[p];
for(int i = 1;i < lowbit(p);i <<= 1){
c_min[p] = min(c_min[p],c_min[p-i]);
c_max[p] = max(c_max[p],c_max[p-i]);
}
p += lowbit(p);
}
}
//若y-lowbit(y) > x ,则query(x,y) = max( h[y] , query(x, y-lowbit(y)) );
//若y-lowbit(y) <=x,则query(x,y) = max( a[y] , query(x, y-1);
int get_min(int l,int r){
int ans = sum[r];
while(l <= r){
ans = min(ans,sum[r]);
r--;
for(;r - l >= lowbit(r);r -= lowbit(r)){
ans = min(ans,c_min[r]);
}
}
return ans;
}
int get_max(int l,int r){
int ans = sum[r];
while(l <= r){
ans = max(ans,sum[r]);
r--;
for(;r - lowbit(r) >= l;r -= lowbit(r)){
ans = max(ans,c_max[r]);
}
}
return ans;
}
int main(){
int T;
scanf("%d",&T);
while(T--){
memset(c_min,0,sizeof(c_min));
memset(c_max,0,sizeof(c_max));
memset(sum,0,sizeof(sum));
scanf("%d%d",&n,&m);
scanf("%s",s+1);
for(int i = 1;i <= n;i ++) a[i] = (s[i] == '+' ? 1 : -1);
for(int i = 1;i <= n;i ++){
sum[i] = sum[i-1] + a[i];
update(i);
}
int l,r;
while(m--){
scanf("%d%d",&l,&r);
int t = sum[r] - sum[l-1];//删掉的区间的贡献
int premax = max(get_max(1,l-1),0);
int maxx = max(premax,get_max(r+1,n)-t);
int premin = min(get_min(1,l-1),0);
int minn = min(premin,get_min(r+1,n)-t);
int ans = maxx - minn + 1;
printf("%d\n",ans);
}
}
return 0;
}