题目大意:有n个数,q个操作。操作有两种:①把一段区间所有数加上p;②查询一段区间内大于等于p的元素的个数。
解题思路:询问次数少,可以用分块解决。
将所有数分成$sqrt{n}$块,对每一块进行排序。
用a数组存原来的数列,b数组存排完序后的数列。
查询时,对左右两个块中的数据暴力查询,中间的由于整块访问并排完序,直接二分查找即可。
修改数据时,对左右两个块中的数据暴力修改,并且更新b数组(重新排序),对中间的,由于相对大小不变,直接打上标记即可。
修改时,最多对两个块进行排序,时间复杂度$O(sqrt{n}log_2sqrt{n})$。
查询时,最多对所有块都进行二分查找,时间复杂度$O(sqrt{n}log_2sqrt{n})$。
故总时间复杂度$O(qsqrt{n}log_2sqrt{n})$。
C++ Code:
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<cctype>
using namespace std;
int a[1000005],b[1000005];
int n,q,size,l[1005],r[1005],blocks,add[1005],inbk[1000005];
char s[4];
inline int readint(){
char c=getchar();
for(;!isdigit(c);c=getchar());
int d=0;
for(;isdigit(c);c=getchar())
d=(d<<3)+(d<<1)+(c^'0');
return d;
}
void chan(int blk){
for(int i=l[blk];i<=r[blk];++i)
b[i]=a[i];
sort(b+l[blk],b+r[blk]+1);
}
int main(){
n=readint();q=readint();
size=(int)(sqrt(n)+0.00000001);
blocks=0;
l[1]=1;
for(int i=1;i<=n;++i){
a[i]=b[i]=readint();
inbk[i]=blocks+1;
if(i%size==0){
r[++blocks]=i;
l[blocks+1]=i+1;
}
}
if(n%size)
r[++blocks]=n;
for(int i=1;i<=blocks;++i)
sort(b+l[i],b+r[i]+1);
memset(add,0,sizeof add);
while(q--){
scanf("%s",s);
if(s[0]=='M'){
int L=readint(),R=readint(),p=readint();
if(L>R)L^=R^=L^=R;
int lft=inbk[L],rgt=inbk[R];
if(lft==rgt){
for(int i=L;i<=R;++i)a[i]+=p;
chan(lft);
}else{
for(int i=lft+1;i<rgt;++i)add[i]+=p;
for(int i=L;i<=r[lft];++i)a[i]+=p;
for(int i=l[rgt];i<=R;++i)a[i]+=p;
chan(lft);
chan(rgt);
}
}else{
int L=readint(),R=readint(),p=readint(),ans=0;
int lft=inbk[L],rgt=inbk[R];
if(lft==rgt){
for(int i=L;i<=R;++i)
if(a[i]+add[lft]>=p)++ans;
}else{
for(int i=lft+1;i<rgt;++i)
ans+=r[i]-(lower_bound(b+l[i],b+r[i]+1,p-add[i])-b-1);
for(int i=L;i<=r[lft];++i)
if(a[i]+add[lft]>=p)++ans;
for(int i=l[rgt];i<=R;++i)
if(a[i]+add[rgt]>=p)++ans;
}
printf("%d
",ans);
}
}
return 0;
}