[bzoj3343]教主的魔法

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[bzoj3343]教主的魔法相关的知识,希望对你有一定的参考价值。

题目描述

教主最近学会了一种神奇的魔法,能够使人长高。于是他准备演示给XMYZ信息组每个英雄看。于是N个英雄们又一次聚集在了一起,这次他们排成了一列,被编号为1、2、……、N。

每个人的身高一开始都是不超过1000的正整数。教主的魔法每次可以把闭区间[L, R](1≤L≤R≤N)内的英雄的身高全部加上一个整数W。(虽然L=R时并不符合区间的书写规范,但我们可以认为是单独增加第L(R)个英雄的身高)

CYZ、光哥和ZJQ等人不信教主的邪,于是他们有时候会问WD闭区间 [L, R] 内有多少英雄身高大于等于C,以验证教主的魔法是否真的有效。

WD巨懒,于是他把这个回答的任务交给了你。

输入输出格式

输入格式:

 

第1行为两个整数N、Q。Q为问题数与教主的施法数总和。

第2行有N个正整数,第i个数代表第i个英雄的身高。

第3到第Q+2行每行有一个操作:

(1) 若第一个字母为“M”,则紧接着有三个数字L、R、W。表示对闭区间 [L, R] 内所有英雄的身高加上W。

(2) 若第一个字母为“A”,则紧接着有三个数字L、R、C。询问闭区间 [L, R] 内有多少英雄的身高大于等于C。

 

输出格式:

 

对每个“A”询问输出一行,仅含一个整数,表示闭区间 [L, R] 内身高大于等于C的英雄数。

 

输入输出样例

输入样例#1:
5 3

1 2 3 4 5

A 1 5 4

M 3 5 1

A 1 5 4
输出样例#1:
2
3

说明

【输入输出样例说明】

原先5个英雄身高为1、2、3、4、5,此时[1, 5]间有2个英雄的身高大于等于4。教主施法后变为1、2、4、5、6,此时[1, 5]间有3个英雄的身高大于等于4。

【数据范围】

对30%的数据,N≤1000,Q≤1000。

对100%的数据,N≤1000000,Q≤3000,1≤W≤1000,1≤C≤1,000,000,000。

 

解题思路

分块打标记,每个块单独排序,查找时每个块内二分答案,更新区间时区间两端重建,中间直接更新标记,复杂度O(n*√n),完毕

 

源代码

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <cmath>
 4 #include <algorithm>
 5 #include <vector>
 6 #define LL long long
 7 using namespace std;
 8 inline int readn(){
 9     int rtn=0,f=1;char ch=getchar();
10     while(ch>9||ch<0){if(ch==-)f=-1;ch=getchar();}
11     while(ch<=9&&ch>=0)rtn=rtn*10+ch-0,ch=getchar();
12     return rtn*f;
13 }
14 inline char readc(){
15     char ch=getchar();
16     while(ch>Z||ch<A)ch=getchar();
17     return ch;
18 }
19 const int N=1001000;
20 int n,q,blo;
21 int v[N],bl[N];
22 int ve[N],atag[N];
23 void reset(int x){
24     int l=(x-1)*blo+1,r=min(n,x*blo)+1;
25     for(int i=l;i<r;i++)
26         ve[i]=v[i];
27     sort(ve+l,ve+r);
28 }
29 int finds(int x,int val){
30     int l=(x-1)*blo+1,r=min(x*blo,n)+1;
31     int rtn=r-1;
32     while(l<r){
33         int mid=(l+r)>>1;
34         if(ve[mid]<val)l=mid+1;
35         else r=mid;
36     }
37     return rtn-l+1;
38 }
39 void add(int l,int r,int m){
40     for(int i=bl[l]+1;i<bl[r];i++)atag[i]+=m;
41     if(bl[l]==bl[r]){
42         for(int i=l;i<=r;i++)v[i]+=m;
43     }
44     else{
45         for(int i=l;i<=bl[l]*blo;i++)v[i]+=m;
46         for(int i=(bl[r]-1)*blo+1;i<=r;i++)v[i]+=m;
47     }
48     reset(bl[l]);reset(bl[r]);
49 }
50 
51 int query(int l,int r,int c){
52     int ans=0;
53     for(int i=bl[l]+1;i<bl[r];i++)ans+=finds(i,c-atag[i]);
54     if(bl[l]==bl[r]){
55         for(int i=l;i<=r;i++)if(v[i]+atag[bl[i]]>=c)ans++;
56     }
57     else {
58         for(int i=l;i<=bl[l]*blo;i++)if(v[i]+atag[bl[i]]>=c)ans++;
59         for(int i=(bl[r]-1)*blo+1;i<=r;i++)if(v[i]+atag[bl[i]]>=c)ans++;
60     }
61     return ans; 
62 }
63 int main(){
64     n=readn();q=readn();blo=sqrt(n);
65     for(int i=1;i<=n;i++){
66         bl[i]=(i-1)/blo+1;
67         v[i]=readn();
68     }
69     for(int i=1;i<=bl[n];i++)reset(i);
70     while(q--){
71         char opt=readc();int l=readn(),r=readn(),c=readn();
72         if(opt==M)add(l,r,c);
73         else printf("%d\n",query(l,r,c));
74     }
75     return 0;
76 }

 

以上是关于[bzoj3343]教主的魔法的主要内容,如果未能解决你的问题,请参考以下文章

BZOJ 3343: 教主的魔法 [分块]学习笔记

BZOJ_3343_教主的魔法_分块+二分查找

[bzoj 3343]教主的魔法

[bzoj3343] 教主的魔法

BZOJ3343: 教主的魔法

[bzoj3343] 教主的魔法