【bzoj3343】教主的魔法 分块+二分

题目描述

教主最近学会了一种神奇的魔法,能够使人长高。于是他准备演示给XMYZ信息组每个英雄看。于是N个英雄们又一次聚集在了一起,这次他们排成了一列,被编号为1、2、……、N
每个人的身高一开始都是不超过1000的正整数。教主的魔法每次可以把闭区间[LR](1≤LRN)内的英雄的身高全部加上一个整数W。(虽然L=R时并不符合区间的书写规范,但我们可以认为是单独增加第LR)个英雄的身高)
CYZ、光哥和ZJQ等人不信教主的邪,于是他们有时候会问WD闭区间 [LR] 内有多少英雄身高大于等于C,以验证教主的魔法是否真的有效。
WD巨懒,于是他把这个回答的任务交给了你。

输入

第1行为两个整数NQQ为问题数与教主的施法数总和。
第2行有N个正整数,第i个数代表第i个英雄的身高。
第3到第Q+2行每行有一个操作:
(1)若第一个字母为“M”,则紧接着有三个数字LRW。表示对闭区间 [LR] 内所有英雄的身高加上W
(2)若第一个字母为“A”,则紧接着有三个数字LRC。询问闭区间 [LR] 内有多少英雄的身高大于等于C

输出

对每个“A”询问输出一行,仅含一个整数,表示闭区间 [LR] 内身高大于等于C的英雄数。

样例输入

5 3
1 2 3 4 5
A 1 5 4
M 3 5 1
A 1 5 4

样例输出

2
3


题解

分块+二分

分成√n 个块,对于每个块另开一个数组将h[i]排序。

修改时,整块直接打add标记,多余部分暴力改,然后重排序。

查询时,整块使用排序后的数组二分查找大于等于c-add的个数,多余部分暴力查询。

注意l和r同块时的特判。

#include <cstdio>
#include <cmath>
#include <algorithm>
using namespace std;
int n , si , h[1000010] , v[1000010] , add[1010];
char str[5];
void reset(int b)
{
	int i , l = b * si , r = min((b + 1) * si - 1 , n - 1);
	for(i = l ; i <= r ; i ++ ) v[i] = h[i];
	sort(v + l , v + r + 1);
}
int search(int b , int lim)
{
	int l = b * si , r = min((b + 1) * si - 1 , n - 1) , mid , ans = r + 1 , tr = r;
	while(l <= r)
	{
		mid = (l + r) >> 1;
		if(v[mid] + add[b] >= lim) ans = mid , r = mid - 1;
		else l = mid + 1;
	}
	return tr - ans + 1;
}
int main()
{
	int	m , i , l , r , x , ans;
	scanf("%d%d" , &n , &m);
	si = (int)sqrt(n);
	for(i = 0 ; i < n ; i ++ ) scanf("%d" , &h[i]);
	for(i = 0 ; i * si < n ; i ++ ) reset(i);
	while(m -- )
	{
		scanf("%s%d%d%d" , str , &l , &r , &x);
		l -- , r -- ;
		if(str[0] == 'M')
		{
			if(l / si == r / si)
			{
				for(i = l ; i <= r ; i ++ ) h[i] += x;
				reset(l / si);
			}
			else
			{
				for(i = l / si + 1 ; i < r / si ; i ++ ) add[i] += x;
				for(i = l ; i < (l / si + 1) * si ; i ++ ) h[i] += x;
				for(i = r / si * si ; i <= r ; i ++ ) h[i] += x;
				reset(l / si) , reset(r / si);
			}
		}
		else
		{
			ans = 0;
			if(l / si == r / si)
				for(i = l ; i <= r ; i ++ )
					ans += (h[i] + add[l / si] >= x);
			else
			{
				for(i = l / si + 1 ; i < r / si ; i ++ ) ans += search(i , x);
				for(i = l ; i < (l / si + 1) * si ; i ++ ) ans += (h[i] + add[l / si] >= x);
				for(i = r / si * si ; i <= r ; i ++ ) ans += (h[i] + add[r / si] >= x);
			}
			printf("%d\n" , ans);
		}
	}
	return 0;
}

 

posted @ 2017-03-20 20:05  GXZlegend  阅读(369)  评论(0编辑  收藏  举报