https://www.luogu.org/problem/show?pid=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的英雄数。
分块大法好。按每个块大小为sqrt(n)分块,另外开一个数组,把原始数组复制过去,块内元素保持有序。对每个块维护一个增量,代表块内的所有元素应该增加的数量,更新时,对完整的块,更新增量即可,不完整的块,直接暴力更新。查询时,对不完整的块,暴力查询,对于完整的块,二分查询块内大于等于c的数量。注意二分的细节,我写的时候是找到第一个大于等于c的位置,然后块的右端减去这个位置再加1,但是块内元素可能全小于c,这时这个位置应该设成超尾的,谨记
#include
using namespace std;
const int N = 1e6 + 10;
int n, m;
int block, sz;
int pos[N], L[N], R[N], add[N];
int a[N], b[N];
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);
}
void init()
{
block = (int)sqrt(n);
sz = n / block;
if(n % block) sz++;
for(int i = 1; i <= n; i++) pos[i] = (i-1) / block + 1;
for(int i = 1; i <= sz; i++)
{
L[i] = (i-1) * block + 1;
R[i] = i * block;
}
R[sz] = n;
for(int i = 1; i <= sz; i++) reset(i);
}
void update(int l, int r, int c)
{
int lb = pos[l], rb = pos[r];
if(lb == rb)
{
for(int i = l; i <= r; i++) a[i] += c;
reset(lb);
}
else
{
for(int i = l; i <= R[lb]; i++) a[i] += c;
for(int i = L[rb]; i <= r; i++) a[i] += c;
for(int i = lb + 1; i < rb; i++) add[i] += c;
reset(lb);
reset(rb);
}
}
int bin_serach(int x, int c)
{
int l = L[x], r = R[x];
int ans = R[x] + 1;//初始设为超尾
while(l <= r)
{
int mid = (l + r) / 2;
if(b[mid] >= c) r = mid - 1, ans = mid;
else l = mid + 1;
}
return R[x] - ans + 1;
}
int query(int l, int r, int c)
{
int lb = pos[l], rb = pos[r];
int ans = 0;
if(lb == rb)
{
for(int i = l; i <= r; i++)
if(a[i] + add[lb] >= c) ans++;
}
else
{
for(int i = l; i <= R[lb]; i++)
if(a[i] + add[lb] >= c) ans++;
for(int i = L[rb]; i <= r; i++)
if(a[i] + add[rb] >= c) ans++;
for(int i = lb + 1; i < rb; i++)
ans += bin_serach(i, c - add[i]);
}
return ans;
}
int main()
{
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; i++) scanf("%d", &a[i]);
init();
char ch;
int a, b, c;
for(int i = 1; i <= m; i++)
{
scanf(" %c%d%d%d", &ch, &a, &b, &c);
if(ch == 'M') update(a, b, c);
else printf("%d\n", query(a, b, c));
}
return 0;
}