「BZOJ3343」教主的魔法(分块+二分查找)
题意:
给定一个数列,您需要支持以下两种操作:
给[l,r]同加一个数
询问[l,r]中有多少数字大于或等于v
(n<=1000000,m<=3000)
题解
块内排序二分查询
修改就用个数组存整块的修改值
不完整的部分都暴力修改和查询
时间复杂度大概O(Qsqrt(n)logn)
1 #include<iostream> 2 #include<cstring> 3 #include<cstdio> 4 #include<cmath> 5 #include<algorithm> 6 using namespace std; 7 const int N=1001000; 8 int n,m,a[N],b[N],block[N],L[2000],R[2000],cnt[2000],ans,Block; 9 int find(int x,int y){ 10 int l=L[x];int r=R[x]; 11 int tmp=9999999; 12 while(l<=r){ 13 int mid=(l+r)>>1; 14 if(b[mid]>=y){ 15 tmp=mid; 16 r=mid-1; 17 } 18 else l=mid+1; 19 } 20 return max(0,R[x]-tmp+1); 21 } 22 int main(){ 23 scanf("%d%d",&n,&m); 24 Block=sqrt(n); 25 for(int i=1;i<=n;i++){ 26 scanf("%d",&a[i]); 27 b[i]=a[i]; 28 block[i]=(i-1)/Block+1; 29 if(!L[block[i]])L[block[i]]=i; 30 R[block[i]]=i; 31 } 32 for(int i=1;i<=block[n];i++)sort(b+L[i],b+1+R[i]); 33 char s[10]; 34 while(m--){ 35 scanf("%s",s); 36 if(s[0]=='A'){ 37 int l,r,c; 38 ans=0; 39 scanf("%d%d%d",&l,&r,&c); 40 if(block[l]==block[r]){ 41 for(int i=l;i<=r;i++)if(a[i]+cnt[block[i]]>=c)ans++; 42 printf("%d\n",ans); 43 continue; 44 } 45 for(int i=l;i<=R[block[l]];i++)if(a[i]+cnt[block[i]]>=c)ans++; 46 for(int i=L[block[r]];i<=r;i++)if(a[i]+cnt[block[i]]>=c)ans++; 47 if(block[l]+1<block[r]) 48 for(int i=block[l]+1;i<=block[r]-1;i++)ans+=find(i,c-cnt[i]); 49 printf("%d\n",ans); 50 } 51 else{ 52 int l,r,c; 53 scanf("%d%d%d",&l,&r,&c); 54 if(block[l]+1<block[r]) 55 for(int i=block[l]+1;i<=block[r]-1;i++)cnt[i]+=c; 56 for(int i=l;i<=R[block[l]];i++)a[i]+=c; 57 for(int i=L[block[l]];i<=R[block[l]];i++)b[i]=a[i]; 58 sort(b+L[block[l]],b+1+R[block[l]]); 59 for(int i=L[block[r]];i<=r;i++)a[i]+=c; 60 for(int i=L[block[r]];i<=R[block[r]];i++)b[i]=a[i]; 61 sort(b+L[block[r]],b+1+R[block[r]]); 62 } 63 } 64 return 0; 65 }