洛谷2801——分块

洛谷2801——分块

题目链接

题目描述
教主最近学会了一种神奇的魔法,能够使人长高。于是他准备演示给XMYZ信息组每个英雄看。于是N个英雄们又一次聚集在了一起,这次他们排成了一列,被编号为1、2、……、N。
每个人的身高一开始都是不超过1000的正整数。教主的魔法每次可以把闭区间[L, R](1≤L≤R≤N)内的英雄的身高全部加上一个整数W。(虽然L=R时并不符合区间的书写规范,但我们可以认为是单独增加第L(R)个英雄的身高)
CYZ、光哥和ZJQ等人不信教主的邪,于是他们有时候会问WD闭区间 [L, R] 内有多少英雄身高大于等于C,以验证教主的魔法是否真的有效。
WD巨懒,于是他把这个回答的任务交给了你。
输入输出格式
输入格式:
第1行为两个整数N、Q。Q为问题数与教主的施法数总和。
第2行有N个正整数,第i个数代表第i个英雄的身高。
第3到第Q+2行每行有一个操作:
(1) 若第一个字母为“M”,则紧接着有三个数字L、R、W。表示对闭区间 [L, R] 内所有英雄的身高加上W。
(2) 若第一个字母为“A”,则紧接着有三个数字L、R、C。询问闭区间 [L, R] 内有多少英雄的身高大于等于C。
输出格式:
对每个“A”询问输出一行,仅含一个整数,表示闭区间 [L, R] 内身高大于等于C的英雄数。
输入输出样例
输入样例#1: 复制
5 3
1 2 3 4 5
A 1 5 4
M 3 5 1
A 1 5 4
输出样例#1: 复制
2
3
说明
【输入输出样例说明】
原先5个英雄身高为1、2、3、4、5,此时[1, 5]间有2个英雄的身高大于等于4。教主施法后变为1、2、4、5、6,此时[1, 5]间有3个英雄的身高大于等于4。
【数据范围】
对30%的数据,N≤1000,Q≤1000。
对100%的数据,N≤1000000,Q≤3000,1≤W≤1000,1≤C≤1,000,000,000。

思想:
这道题直接暴力很简单,主要时间肯定超,其中有两个很耗时的过程:

  1. 增加;这个问题可以用一个add数组进行优化:如果出现对某一整块进行增加的时候,不进行这次操作,直接处理add的值。
  2. 查询;用二分搜索块内,(其实C++自己有lower_bound,但是,好像自己写的速度会更高一点,我也不知道什么原因,反正这道题都能用)。

上代码:

# include <iostream>
# include <algorithm>
# include <cstdio>
# include <cmath>
using namespace std;
const int maxn = 1e6 + 9;
int n, q, a[maxn], b[maxn], l[maxn], r[maxn], belong[maxn], add[maxn], block, num;
void reset(int x) {
	for (int i = l[x]; i <= r[x]; i++) {
		b[i] = a[i];
	}
	sort(b + l[x], b + r[x] + 1);
}
int fin(int x, int val) {
	int ll = l[x], rr = r[x], mid;
	while (ll <= rr) {
		mid = (ll + rr) / 2;
		if (b[mid] < val)
			ll = mid + 1;
		else
			rr = mid - 1;
	}
	return r[x] - ll + 1;
}
void bulid(void) {
	block = int (sqrt(n));
	num = n / block;
	if (n%block) {
		num++;
	}
	for (int i = 1; i <= num; i++) {
		l[i] = (i - 1) * block + 1;
		r[i] = i * block;
	}
	r[num] = n;
	for (int i = 0; i <= num; i++) {
		sort(b + l[i], b + r[i] + 1);
	}
	for (int i = 1; i <= n; i++) {
		belong[i] = (i-1) / block+1;
	}
}
void update(int left,int right,int val) {
	if (belong[left] == belong[right]) {
		for (int i = left; i <= right; i++) {
			a[i] += val;
		}
		reset(belong[left]);
		return;
	}
	for (int i = left; i <= r[belong[left]]; i++) {
		a[i] += val;
	}
	reset(belong[left]);
	for (int i = belong[left] + 1; i < belong[right]; i++) {
		add[i] += val;
	}
	for (int i = l[belong[right]]; i <= right; i++) {
		a[i] += val;
	}
	reset(belong[right]);
}
int ask(int left, int right, int val) {
	int sum = 0;
	if (belong[left] == belong[right]) {
		for (int i = left; i <= right; i++) {
			if (a[i] + add[belong[i]] >= val) {
				sum++;
			}
		}
		return sum;
	}
	for (int i = left; i <= r[belong[left]]; i++) {
		if (a[i] + add[belong[i]] >= val) {
			sum++;
		}
	}
	for (int i = belong[left] + 1; i < belong[right]; i++) {
		sum +=fin(i, val-add[i]);
	}
	for (int i = l[belong[right]]; i <= right; i++) {
		if (a[i] + add[belong[i]] >= val) {
			sum++;
		}
	}
	return sum;
}
int main() {
	cin >> n >> q;
	for (int i = 1; i <= n; i++) {
		cin >> a[i];
		b[i] = a[i];
	}
	char str[5];
	int left, right, val;
	bulid();
	for (int i = 0; i < q; i++) {
		scanf("%s%d%d%d", str, &left, &right, &val);
		if (str[0] == 'M') {
			update(left, right, val);
		}
		else if (str[0]=='A'){
			cout << ask(left, right, val) << endl;;
		}
		else{}
	}
	return 0;
}

目前已过。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值