【题目】
题目描述:
教主最近学会了一种神奇的魔法,能够使人长高。于是他准备演示给 XMYZ 信息组每个英雄看。于是 n n n 个英雄们又一次聚集在了一起,这次他们排成了一列,被编号为 1 , 2 , … … , n 1,2,……,n 1,2,……,n。
每个人的身高一开始都是不超过 1000 1000 1000 的正整数。教主的魔法每次可以把闭区间 [ l l l , , , r r r ]( 1 ≤ l ≤ r ≤ n 1≤l≤r≤n 1≤l≤r≤n)内的英雄的身高全部加上一个整数 w w w。(虽然 l = r l=r l=r 时并不符合区间的书写规范,但我们可以认为是单独增加第 l l l( r r r)个英雄的身高)
CYZ、光哥和 ZJQ 等人不信教主的邪,于是他们有时候会问 WD 闭区间 [ l l l , , , r r r ] 内有多少英雄身高大于等于 c c c,以验证教主的魔法是否真的有效。
WD 巨懒,于是他把这个回答的任务交给了你。
输入格式:
第 1 1 1 行为两个整数 n n n、 q q q。 q q q 为问题数与教主的施法数总和。
第 2 2 2 行有 n n n 个正整数,第 i i i 个数代表第 i i i 个英雄的身高。
第 3 3 3 到第 q + 2 q+2 q+2 行每行有一个操作:
- 若第一个字母为 “M”,则紧接着有三个数字 l l l、 r r r、 w w w。表示对闭区间 [ l l l , , , r r r ] 内所有英雄的身高加上 w w w。
- 若第一个字母为 “A”,则紧接着有三个数字 l l l、 r r r、 c c c。询问闭区间 [ l l l , , , r r r ] 内有多少英雄的身高大于等于 c c c。
输出格式:
对每个 “A” 询问输出一行,仅含一个整数,表示闭区间 [ l l l , , , r r r ] 内身高大于等于 c c c 的英雄数。
样例数据:
输入
5 3
1 2 3 4 5
A 1 5 4
M 3 5 1
A 1 5 4
输出
2
3
说明:
【输入输出样例说明】
原先 5 5 5 个英雄身高为 1 1 1、 2 2 2、 3 3 3、 4 4 4、 5 5 5,此时 [ 1 1 1 , , , 5 5 5 ] 间有 2 2 2 个英雄的身高大于等于 4 4 4。教主施法后变为 1 1 1、 2 2 2、 4 4 4、 5 5 5、 6 6 6,此时 [ 1 1 1 , , , 5 5 5 ] 间有 3 3 3 个英雄的身高大于等于 4 4 4。
【数据范围】
对 30 % 30\% 30% 的数据, n ≤ 1000 n≤1000 n≤1000, q ≤ 1000 q≤1000 q≤1000。
对 100 % 100\% 100% 的数据, n ≤ 1000000 n≤1000000 n≤1000000, q ≤ 3000 q≤3000 q≤3000, 1 ≤ w ≤ 1000 1≤w≤1000 1≤w≤1000, 1 ≤ c ≤ 1 , 000 , 000 , 000 1≤c≤1,000,000,000 1≤c≤1,000,000,000。
【分析】
分块基础题
区间加就是基本操作,主要是询问怎么做
对于每个块,除了用 a i a_i ai 维护原序列外,还要用一个数组维护对每个块内排序后的序列
例如,对于序列 5 , 4 , 3 , 2 , 1 5,4,3,2,1 5,4,3,2,1,排序后的序列就是 4 , 5 , 2 , 3 , 1 4,5,2,3,1 4,5,2,3,1
有了这个之后,就可以用 lower_bound 快速统计整块内的询问,对于散块直接暴力查询就行了
时间复杂度 O ( q n    log 2 n ) O(q\sqrt n\;\log_2\sqrt n) O(qnlog2n),足以解决这道题
【代码】
#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define W 1005
#define N 1000005
#define Sqrtn 1005
using namespace std;
char op[5];
int n,q,S,num,a[N],A[N],L[N],R[N],be[N],add[Sqrtn];
void rebuild(int x)
{
int i;
for(i=L[x];i<=R[x];++i) A[i]=a[i];
sort(A+L[i],A+R[i]+1);
}
void Add(int l,int r,int x)
{
int i;
if(be[r]-be[l]<2)
{
for(i=l;i<=r;++i) a[i]+=x;
rebuild(be[l]),rebuild(be[r]);
return;
}
for(i=l;i<=R[be[l]];++i) a[i]+=x;
for(i=L[be[r]];i<=r;++i) a[i]+=x;
for(i=be[l]+1;i<=be[r]-1;++i) add[i]+=x;
rebuild(be[l]),rebuild(be[r]);
}
int query(int l,int r,int x)
{
int i,ans=0;
if(be[r]-be[l]<2)
{
for(i=l;i<=r;++i)
if(a[i]+add[be[i]]>=x)
ans++;
return ans;
}
for(i=l;i<=R[be[l]];++i) if(a[i]+add[be[i]]>=x) ans++;
for(i=L[be[r]];i<=r;++i) if(a[i]+add[be[i]]>=x) ans++;
for(i=be[l]+1;i<=be[r]-1;++i) ans+=(R[i]+1-(lower_bound(A+L[i],A+R[i]+1,x-add[i])-A));
return ans;
}
int main()
{
int l,r,i,x;
scanf("%d%d",&n,&q);
S=sqrt(n);
for(i=1;i<=n;++i)
{
be[i]=(i-1)/S+1;
scanf("%d",&a[i]);
}
for(i=1;;++i)
{
num++;
L[i]=(i-1)*S+1,R[i]=i*S;
if(R[i]>=n) {R[i]=n;break;}
}
memcpy(A,a,sizeof(A));
for(i=1;i<=num;++i)
sort(A+L[i],A+R[i]+1);
for(i=1;i<=q;++i)
{
scanf("%s%d%d%d",op,&l,&r,&x);
if(op[0]=='M') Add(l,r,x);
else printf("%d\n",query(l,r,x));
}
return 0;
}

本文介绍一种高效算法,通过分块预处理和维护排序序列,实现区间元素加值和查询身高大于特定值的英雄数量。适用于大量更新和查询操作,时间复杂度O(q√n log_2 √n)。
758

被折叠的 条评论
为什么被折叠?



