【分块】二

于是分块就来到了第二题。

首先还是感谢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\n",query(l,r,x));
66     }
67     return 0;
68 }

(以上程序感谢xzp同学嘤嘤嘤)

posted @ 2017-05-23 16:53  Hathawaxy  阅读(185)  评论(0编辑  收藏  举报