Once upon a time,COVID-19席卷全球,Chinese Government要求学校复课时必须测量学生体温
YC中学有几万名同窗,要找到发烧的同窗进行隔离 若是要让一位老师完成全部测温任务,那这将是一个大工程,效率会很低(左图)
因此将学校全部同窗分红班级进行,而后汇总,效率会更高(右图)c++
刚刚引中咱们说的:算法
将学校全部同窗分红班级进行,而后汇总数组
这就是一种分块
那问题来了,什么是分块呢?
其实经过刚刚的情景,你已经领悟到了分块的本质:函数
将一个总体划分为若干个小块,进行处理优化
算法中,与之对应的就是:spa
总体 | 小块 |
---|---|
学校 | 班级 |
数组 | 若干元素 |
那么,分块究竟是怎么一种思想呢?
整块维护,残块查找code
仍是以测量体温举例:blog
如今YC中学要查找体温在36℃~37.5℃区间内的同窗
怎么作呢?
不可能又去挨个同窗去统计、去数吧
那就作一张大表吧,在以前测温的时候就把34 ~ 35℃、35 ~36℃、36 ~ 37℃、37 ~ 38℃、38 ~ 39℃......的同窗分别列出来,数量分别加出来
而后36 ~ 37℃能够就直接在表里查出人数
那37 ~ 37.5℃怎么办呢?
表内并无37 ~ 37.5℃的这样0.5大小的区间啊
那就在37 ~ 38℃这个区间去找呗
方法能够直接暴力遍历,也能够二分查找等等图片
刚刚解决的问题就是一个典型的分块
像34 ~ 35℃、35 ~36℃、36 ~ 37℃、37 ~ 38℃、38 ~ 39℃这种列在表上给出的就是整块
37 ~ 37.5℃这种表上没有,包含在一个其余整块中的但又不足一个整块的就叫作残块ip
不难发现,其实分块这个思想是一种暴力,一种优化的暴力,但每每也颇有效
Such as 线段树过于臃肿,代码冗长,大材小用;而直接暴力就会TLE,不能知足数据大小
这就很适合分块了
那么咱们具体怎么作呢?
咱们先要求得应该分为多少个区块嘛,而后求得每一个区块应该包含多少个元素
而后在输入时分块
要使状况最优,那么区块既不能太少也不能太多
太少,整块的数量会太少,花费大量的时间处理残块
太多,区块的长度会过短,失去总体处理区块的意义
因此,咱们取块数为根号n
而开平方开不尽的n,咱们一般在最后接一个不足整块元素的假整块(能够看作整块)
这样在最坏状况下
咱们要处理接近根号n个整块,还要对长度为 2倍根号n 的残块最后单独处理
cin>>n; blo=sqrt(n);//sqrt()开平方函数 for(int i=1;i<=n;i++){ cin>>a[i];//储存元素a[i] pos[i]=(i-1)/blo+1;//pos[i]为记录元素a[i]属于第几个整块 m[pos[i]]=max(a[i],m[pos[i]]);//寻找第pos[i]个整块的最大值存入m[pos[i]] }
咱们先统计左右残块,而后再统计整块
cin>>q; int l,r; while(q--){ cin>>l>>r; l++; r++; int ans=0; for(int i=l;i<=min(r, pos[l]*blo);i++){//统计左残缺块 ans=max(ans,a[i]); } if(pos[l]!=pos[r]){//存在右残缺块 for(int i=(pos[r]-1)*blo+1;i<=r;i++){//统计右残缺块 ans=max(ans,a[i]); } } for( int i=pos[l]+1;i<=pos[r]-1;i++){//统计中间整块 ans=max(ans,m[i]); } cout<<ans<<endl; }
先看一个例题
分块入门之求最大值
Input
第一行给出一个数字N,接下来N+1行,每行给出一个数字Ai,(1<=i<=N<=1E5)
接来给出一个数字Q(Q<=7000),表明有Q个询问
每组询问格式为a,b即询问从输入的第a个数到第b个数,其中的最大值是多少
Output
如题所述
Sample Input
10 0 1 2 3 2 3 4 3 2 1 0 5 0 10 2 4 3 7 7 9 8 8
Sample Output
4 3 4 3 2
模板题,而后刚刚已经讲过了这个代码
惟一的坑就在于接下来N+1行都是数字Ai
也就是有n+1个数字Ai
也就是n须要n++
#include <bits/stdc++.h> using namespace std; int n; int a[101000]; int q; int blo; int pos[101000]; int m[101000]; //blo为区间大小,pos[i]表示a[i]元素位于第pos[i]块,m[i]表示区块最大值 int main(){ cin>>n; n++; blo=sqrt(n); for(int i=1;i<=n;i++){ cin>>a[i]; pos[i]=(i-1)/blo+1; m[pos[i]]=max(a[i],m[pos[i]]); } cin>>q; int l,r; while(q--){ cin>>l>>r; l++; r++; int ans=0; for(int i=l;i<=min(r, pos[l]*blo);i++){//统计左残缺块 ans=max(ans,a[i]); } if(pos[l]!=pos[r]){//存在右残缺块 for(int i=(pos[r]-1)*blo+1;i<=r;i++){//统计右残缺块 ans=max(ans,a[i]); } } for( int i=pos[l]+1;i<=pos[r]-1;i++){//统计中间整块 ans=max(ans,m[i]); } cout<<ans<<endl; } return 0; }
[Noip模拟题]教主的魔法
Description
教主最近学会了一种神奇的魔法,可以令人长高
因而他准备演示给XMYZ信息组每一个英雄看
因而N个英雄们又一次汇集在了一块儿
此次他们排成了一列,被编号为一、二、……、N
每一个人的身高一开始都是不超过1000的正整数
教主的魔法每次能够把闭区间[L, R](1≤L≤R≤N)内的英雄的身高所有加上一个整数W
(虽然L=R时并不符合区间的书写规范,但咱们能够认为是单独增长第L(R)个英雄的身高)
CYZ、光哥和ZJQ等人不信教主的邪
因而他们有时候会问WD闭区间 [L,R] 内有多少英雄身高大于等于C
以验证教主的魔法是否真的有效
WD巨懒,因而他 把这个回答的任务交给了你
Input
第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
N≤1000000,Q≤3000,1≤W≤1000,1≤C≤1,000,000,000
Output
对每一个"A"询问输出一行,仅含一个整数,表示闭区间 [L, R] 内身高大于等于C的英雄数。Sample Input
5 3 1 2 3 4 5 A 1 5 4 M 3 5 1 A 1 5 4
Sample Output
2 3
【输入输出样例说明】
原先5个英雄身高为一、二、三、四、5,此时[1, 5]间有2个英雄的身高大于等于4
教主施法后变为一、二、四、五、6,此时[1, 5]间有3个英雄的身高大于等于4
不少元素,进行增长、查找最大值操做
多了一个修改操做,不是很难
同理像查找这样整块维护,残块增长
咱们就再增长一个数组,统一记录每一个整块变化量是多少
记录每一个整块的变化量,而后最后找最值的时候,单个整块的最值加上或者减去变化量比较就能够了
残块的单个元素就直接加上或者减去变化量,找最值
void update(int x,int y,int v){ if(pos[x]==pos[y]){ for(int i=x;i<=y;i++)a[i]=a[i]+v; } else{ for(int i=x;i<=pos[x]*block;i++)a[i]=a[i]+v; for(int i=(pos[y]-1)*block+1;i<=y;i++)a[i]=a[i]+v; } reset(pos[x]);reset(pos[y]); for(int i=pos[x]+1;i<pos[y];i++) add[i]+=v; }
#include<bits/stdc++.h> using namespace std; int n; int q,m,block; int a[1010000],b[1010000],pos[1010000],add[1010000]; void reset(int x){ int l=(x-1)*block+1,r=min(x*block,n); for(int i=l;i<=r;i++) b[i]=a[i]; sort(b+l,b+r+1); } int find(int x,int v){ int l=(x-1)*block+1,r=min(x*block,n); int last=r; while(l<=r){ int mid=(l+r)>>1; if(b[mid]<v)l=mid+1; else r=mid-1; } return last-l+1; } void update(int x,int y,int v){ if(pos[x]==pos[y]){ for(int i=x;i<=y;i++)a[i]=a[i]+v; } else{ for(int i=x;i<=pos[x]*block;i++)a[i]=a[i]+v; for(int i=(pos[y]-1)*block+1;i<=y;i++)a[i]=a[i]+v; } reset(pos[x]);reset(pos[y]); for(int i=pos[x]+1;i<pos[y];i++) add[i]+=v; } int query(int x,int y,int v){ int sum=0; if(pos[x]==pos[y]){ for(int i=x;i<=y;i++)if(a[i]+add[pos[i]]>=v)sum++; } else { for(int i=x;i<=pos[x]*block;i++) if(a[i]+add[pos[i]]>=v)sum++; for(int i=(pos[y]-1)*block+1;i<=y;i++) if(a[i]+add[pos[i]]>=v)sum++; } for(int i=pos[x]+1;i<pos[y];i++) sum+=find(i,v-add[i]); return sum; } int main(){ cin>>n>>q; block=int(sqrt(n)); for(int i=1;i<=n;i++){ cin>>a[i]; pos[i]=(i-1)/block+1; b[i]=a[i]; } if(n%block)m=n/block+1; else m=n/block; for(int i=1;i<=m;i++)reset(i); for(int i=1;i<=q;i++){ char ch[5];int x,y,v; cin>>ch>>x>>y>>v; if(ch[0]=='M'){ update(x,y,v); }else{ cout<<query(x,y,v)<<endl; } } return 0; }
END