Educational Codeforces Round 102 (Rated for Div. 2) D. Program (区间最值 + 思维)

在这里插入图片描述
题意:给定你一个长度为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;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值