于是分块就来到了第二题。
首先还是感谢hzw学长。
以下是题面(感谢zcr xyf xzp小组)
小M的简单题(easy.pas/.cpp/.c)
时间限制:3s 内存限制:128MB
题目描述:
小M是某知名高中的学生,有一天,他请他的n个同学吃苹果,同学们排成一行,且手中已经有一些苹果。为了表示他的大方,有时他会给l到r的同学x个苹果,但为了了解分配的情况,有时他会询问l到r的同学中拥有的苹果数小于x的人的个数。现在,小M想让你帮他解决这道简单题。
输入格式:
第一行:两个整数n,m表示n个同学,m组询问。
第二行:n个数,a[1],a[2]...a[n],a[i]表示第i个同学一开始手中的苹果数。(0<=a[i]<=3e4)
第3~m+2行:每行表示一组询问,格式为C l r x表示给l到r的同学x个苹果,或者Q l r x表示询问l到r的同学中拥有的苹果数小于x的人的个数。(1<=l<=r<=n,0<=x<=3e4)
输出格式:
每行一个数,输出l到r的同学中拥有的苹果数小于x的人的个数。
样例输入1:
5 5
1 6 3 2 3
Q 1 3 3
C 1 2 2
Q 3 4 3
C 2 3 1
Q 2 3 4
样例输出1:
1
1
0
样例输入2:
5 4
2 3 1 3 4
C 4 5 3
C 1 5 1
C 2 3 2
Q 1 3 4
样例输出2:
1
数据范围:
|
N |
M |
特殊说明 |
第1~3组 |
1000 |
1000 |
/ |
第4组 |
30000 |
1000 |
r-l<=1000 |
第5组 |
30000 |
1000 |
询问均在修改后 |
第6组 |
30000 |
30000 |
每次的x相同 |
第7~10组 |
30000 |
30000 |
/ |
【简述】
给出一个长为n的数列,以及n个操作,操作涉及区间加法,询问区间内小于某个值x的元素个数。
【题解】
有了上一题的经验,我们可以发现,数列简单分块问题实际上有三项东西要我们思考:
对于每次区间操作:
1.不完整的块 的O(√n)个元素怎么处理?
2.O(√n)个 整块 怎么处理?
3.要预处理什么信息(复杂度不能超过后面的操作)?
我们先来思考只有询问操作的情况,不完整的块枚举统计即可;而要在每个整块内寻找小于一个值的元素数,于是我们不得不要求块内元素是有序的,这样就能使用二分法对块内查询,需要预处理时每块做一遍排序,复杂度O(nlogn),每次查询在√n个块内二分,以及暴力2√n个元素,总复杂度O(nlogn + n√nlog√n)。
可以通过均值不等式计算出更优的分块大小,就不展开讨论了
那么区间加怎么办呢?
套用第一题的方法,维护一个加法标记,略有区别的地方在于,不完整的块修改后可能会使得该块内数字乱序,所以头尾两个不完整块需要重新排序,复杂度分析略。
在加法标记下的询问操作,块外还是暴力,查询小于(x – 加法标记)的元素个数,块内用(x – 加法标记)作为二分的值即可。
标程:
1 #include<cmath> 2 #include<vector> 3 #include<cstdio> 4 #include<algorithm> 5 using namespace std; 6 vector<long long>v[1005]; 7 int blo,n,m,l,r,x,bl[100005]; 8 long long a[100005],tag[100005]; 9 char op[105]; 10 void reset(int x) 11 { 12 v[x].clear(); 13 for (int i=(x-1)*blo+1; i<=min(x*blo,n); i++) 14 v[x].push_back(a[i]); 15 sort(v[x].begin(),v[x].end()); 16 } 17 void add(int l,int r,int x) 18 { 19 for (int i=l; i<=min(bl[l]*blo,r); i++) 20 a[i]+=x; 21 reset(bl[l]); 22 if (bl[l]!=bl[r]) 23 { 24 for (int i=(bl[r]-1)*blo+1; i<=r; i++) 25 a[i]+=x; 26 reset(bl[r]); 27 } 28 for (int i=bl[l]+1; i<=bl[r]-1; i++) 29 tag[i]+=x; 30 } 31 int query(int l,int r,int x) 32 { 33 int ans=0; 34 for (int i=l; i<=min(bl[l]*blo,r); i++) 35 if (a[i]+tag[bl[l]]<x) ans++; 36 if (bl[l]!=bl[r]) 37 { 38 for (int i=(bl[r]-1)*blo+1; i<=r; i++) 39 if (a[i]+tag[bl[r]]<x) ans++; 40 } 41 for (int i=bl[l]+1; i<=bl[r]-1; i++) 42 { 43 int c=x-tag[i]; 44 ans+=lower_bound(v[i].begin(),v[i].end(),c)-v[i].begin(); 45 } 46 return ans; 47 } 48 int main() 49 { 50 scanf("%d%d",&n,&m); 51 blo=sqrt(n); 52 for (int i=1; i<=n; i++) 53 { 54 scanf("%lld",&a[i]); 55 bl[i]=(i-1)/blo+1; 56 v[bl[i]].push_back(a[i]); 57 } 58 for (int i=1; i<=bl[n]; i++) 59 sort(v[i].begin(),v[i].end()); 60 for (int i=1; i<=m; i++) 61 { 62 scanf("%s",op); 63 scanf("%d%d%d",&l,&r,&x); 64 if (op[0]=='C') add(l,r,x); 65 else printf("%d ",query(l,r,x)); 66 } 67 return 0; 68 }
(以上程序感谢xzp同学嘤嘤嘤)