分块入门学习
学习博客:http://hzwer.com/8053.html
分块,顾名思义,就是把很长的一整个区间,分成几个小的区间去处理,降低时间复杂度。(其实就和莫队思想差不多)
那么我们究竟分成多少块呢?答案是n−−√n。
因为这样我们分出来的每一段区间的数字个数就是n−−√n。
区间个数和每个区间所维护的数字个数相等,所以时间复杂度是比较优的。
比如n=100的时候,分成了10个区间,每个区间维护10个数。
有人问n=105的时候怎么办,那我们就多分一个区间,共11个区间,前10个区间每个区间维护10个数,第11个区间维护5个数就好了(换句话说就是余数单独分一个区间,并且可知余数不超过n−−√n)。
我们先看一道裸题。
初始时给你n个数字(n<=100000n<=100000),至多q次操作(q<=100000q<=100000),操作分为两种:
1 x y表示将第x个数字加上y
2 x y表示求[x,y]区间的最大值
有人一看卧槽这不是线段树或树状数组裸题吗?是的,线段树和树状数组完全可以做,但是分块怎么做呢?
可能大家一下子就想出来了,我们将n个数字分成n−−√n个区间之后,每个区间维护一个最大值,在第一种操作的时候,我们修改一下单点的值并维护所在的区间,在第二种操作时,我们扫描一遍从x到y的区间,注意,我们并不是暴力地扫描每一个点,而是如果某个区间被包括在[x,y]的范围内,我们就直接看区间维护的最大值就可以了,而对于只有一部分在[x,y]范围内的区间,我们就暴力看每个数就行了。
我们可以推出这样做时间复杂度是nn−−√nn
为什么呢,单点更新的时候,显然是O(1)O(1)
而查询的时候,对于全部包含在[x,y]中的区间,我们至多需要查看n−−√n个区间,而对于不完全被包含在[x,y]中的区间,我们暴力地去查看,也只需要看至多两个区间(左端点和右端点所在的区间),共2(n−−√−1)2(n−1)个数,所以总的时间复杂度还是n−−√n
nn次操作,那就是O(nn−−√)O(nn)
我们可以看到这个时间复杂度是不如线段树O(nlogn)O(nlogn)的,但是也是可以接受,并且能过很多题的。
#include <iostream> #include <math.h> #include <string.h> #include <stdio.h> #include <algorithm> using namespace std; const int maxn = 100050; int n,q,block,l[maxn],r[maxn],num,belong[maxn],Max[maxn]; //block表示每个区间所维护的数的个数,num表示区间个数 //l[i]和r[i]表示第i个区间的左右端点,belong[i]表示第i个值所在的区间 //Max[i]表示第i个区间的最大值 int a[maxn]; void build() { int block=sqrt(n); int num=n/block; if(n%block)//如果有余数则需要另开一个区间 num++; for(int i=1;i<=num;i++) { l[i]=(i-1)*block+1; r[i]=i*block; } r[num]=n;//最后一个区间右端点特殊处理 memset(Max,0,sizeof(Max)); for(int i=1;i<=n;i++)//初始化Max数组 { belong[i]=(i-1)/block+1; Max[belong[i]]=max(Max[belong[i]],a[i]); } } void update(int pos,int y) { a[pos]+=y; Max[belong[pos]]=max(Max[belong[pos]],a[pos]); } int query(int x,int y) { int ll=belong[x]; int rr=belong[y]; if(ll==rr)//x和y在同一个区间的情况 { int ans=0; for(int i=x;i<=y;i++) ans=max(ans,a[i]); return ans; } else { int ans=0; for(int i=x;i<=r[ll];i++)//处理x所在的区间 ans=max(ans,a[i]); for(int i=ll+1;i<=rr-1;i++)//中间的区间 ans=max(ans,Max[i]); for(int i=l[rr];i<=y;i++)//处理y所在的区间 ans=max(ans,a[i]); return ans; } } int main() { scanf("%d%d",&n,&q); for(int i=1;i<=n;i++) scanf("%d",&a[i]); build(); while(q--) { int t,x,y; scanf("%d%d%d",&t,&x,&y); if(t==1) update(x,y); else printf("%d\n",query(x,y)); } return 0; }