题目描述
教主最近学会了一种神奇的魔法,能够使人长高。于是他准备演示给XMYZ信息组每个英雄看。于是N个英雄们又一次聚集在了一起,这次他们排成了一列,被编号为1、2、……、N。
每个人的身高一开始都是不超过1000的正整数。教主的魔法每次可以把闭区间[L, R](1≤L≤R≤N)内的英雄的身高全部加上一个整数W。(虽然L=R时并不符合区间的书写规范,但我们可以认为是单独增加第L(R)个英雄的身高)
CYZ、光哥和ZJQ等人不信教主的邪,于是他们有时候会问WD闭区间 [L, R] 内有多少英雄身高大于等于C,以验证教主的魔法是否真的有效。
WD巨懒,于是他把这个回答的任务交给了你。
--by luogu
http://www.luogu.org/problem/show?pid=2801
分块的模板题:
读入序列,分块存一次(也就是存了两次),块内维护大小单调,块间位置不变;
有两操作:
- 区间加
- 区间大于k的个数;
对于1:
把整块属于该区间的部分的增加做成标记;
然后对区间最左最右两段,她们可能不是一个整块,把她们的原序列一个点一个点修改,然后把这两个块清了重建;
对于2:
基于块内单调;
对于整块属于该区间的部分二分查找,注意查找时考虑加入块的标记;
然后对区间最左最右两段,她们可能不是一个整块,怎么办呢;
怎么办呢?
对着原序列数组相应的位置一个点一个点地判断就好了;
需要注意的是块内需要维护单调,
怎么维护呢?
对每个新建/重建的块排序就好了;
反正有大把时光
反正时间过得去
代码如下:
1 #include<cstdio> 2 #include<cmath> 3 #include<algorithm> 4 using namespace std; 5 int n,q,cut,p; 6 int a[1000010]; 7 int brick[1010][1010]; 8 int mark[2010]; 9 void add(int ,int ,int ); 10 void fin(int ,int ,int ); 11 int find(int ,int ); 12 int main() 13 { 14 int i,j,k,l,r,wc; 15 scanf("%d%d",&n,&q); 16 cut=(int)sqrt(n); 17 if(cut*cut<n)cut++; 18 for(i=1;i<=n;i++){ 19 scanf("%d",&a[i]); 20 brick[i/cut][i%cut]=a[i]; 21 } 22 for(i=0;i<=cut-1;i++) 23 sort(brick[i],brick[i]+cut); 24 for(i=1;i<=q;i++){ 25 char c[10]; 26 scanf("%s%d%d%d",&c,&l,&r,&wc); 27 if(c[0]=='A') 28 fin(l,r,wc); 29 else 30 add(l,r,wc); 31 } 32 } 33 void add(int l,int r,int w){ 34 int bl=l/cut,br=r/cut; 35 int i,j,k,ll=l%cut,rr=r%cut; 36 for(i=0;i<=cut-1;i++){ 37 brick[bl][i]=a[bl*cut+i]; 38 brick[br][i]=a[br*cut+i]; 39 if(bl!=br){ 40 if(i>=ll){ 41 brick[bl][i]+=w; 42 a[bl*cut+i]+=w; 43 } 44 if(i<=rr){ 45 brick[br][i]+=w; 46 a[br*cut+i]+=w; 47 } 48 } 49 else 50 if(i>=ll&&i<=rr){ 51 brick[br][i]+=w; 52 a[br*cut+i]+=w; 53 } 54 } 55 sort(brick[br],brick[br]+cut); 56 sort(brick[bl],brick[bl]+cut); 57 bl++;br--; 58 for(i=bl;i<=br;i++) 59 mark[i]+=w; 60 return ; 61 } 62 void fin(int l,int r,int c){ 63 int ans=0,i,j,k; 64 int bl=l/cut,br=r/cut,ll=l%cut,rr=r%cut; 65 if(bl==br){ 66 for(j=l;j<=r;j++) 67 if(a[j]+mark[bl]>=c) 68 ans++; 69 printf("%d ",ans); 70 return ; 71 } 72 for(i=ll,j=l;i<=cut-1;i++,j++) 73 if(a[j]+mark[bl]>=c) 74 ans++; 75 for(i=rr,j=r;i>=0;i--,j--) 76 if(a[j]+mark[br]>=c) 77 ans++; 78 bl++;br--; 79 for(i=bl;i<=br;i++) 80 ans+=cut-find(i,c-mark[i]); 81 printf("%d ",ans); 82 return ; 83 } 84 int find(int num,int x){ 85 int mid,L=0,R=cut-1; 86 while(L<R){ 87 mid=(L+R)>>1; 88 if(brick[num][mid]<x) 89 L=mid+1; 90 else 91 R=mid; 92 } 93 if(brick[num][L]<x) 94 L++; 95 return L; 96 }
//10 10 //1 1 1 8 6 5 3 5 2 6 //Q 1 2 5650 //A 3 8 0 //A 6 8 4 //A 3 9 6 //A 1 6 9 //Q 3 9 2403 //A 4 7 4 //Q 2 5 3066 //A 5 7 2 //A 1 8 2