对于分块学习,主要是因为今天有位大牛Tom栋爷在讲根号级别复杂度的算法时,就是以分块来作为基础讲的,然而因为当时我并不懂分块,所以在听讲是一脸懵逼。晚上赶紧恶补一番,入了门,刷了道题。
进入正题
分块,就是将一段信息(数据结构,序列)分成几块处理,当然最基础的是序列上的分块,再高层点的就是树上的分块,分块套分块,分块套数据结构以及数据结构套分块。
因为我是初学者,今天就记录下最基础的分块中的最基础的部分,好让自己以后忘了可以看看自己的博客。
用题目来记录是最有效的
给一个长度为n的序列,每次会给其一段区间增加一个值,然后单点查询。
解法一:线段树,区间修改与单点查询,时间复杂度:每次查询修改
O(logn)
+建树
O(nlogn)
。
解法二:树状数组,经典的改段求点,时间复杂度为每次查询修改
O(logn)
Segmen Tree来讲,常数较小。
当然了今天的重点应该是用分块来解决。
分块解法
我们将这长度为n的序列每m个分成一块,那么就有n/m个块,以及会多出不超过m个单位的数。
在区间修改时,先把这段区间内的每个块修改,用线段树的标记思想,打下标记,然后多出的那些不完整的部分直接暴力修改权值。
时间复杂度是:修改块
O(n/m)
+修改散数
O(m)
,总
O(n/m+m)
。
运用一些数学知识(均值不等式),当m为
n√
时,总复杂度最低。
分块:
block=sqrt(n); //每一个块的个数。
fo(i,1,n) bl[i]=(i-1)/block+1; //bl数组表示第i个位置属于的块是哪个
bl[0]=1; bl[n+1]=bl[n]; //我会处理边界
fo(i,1,n){
int x,y;
scanf("%d%d",&x,&y);
add(x,y); //区间修改
}
fo(i,1,n-1) printf("%d ",a[i]+lab[bl[i]]); //答案是本身的值加块上的标记的值
printf("%d\n",a[n]+lab[bl[n]]);//这是在交题时的格式问题,直接忽略就是了
区间修改:
void add(int a,int b,int c)
{
bool bz1,bz2;//我比较弱,用bz1,bz2来记录x,y是否是处于各自块的边界
if (bl[a-1]==bl[a]) bz1=false;
else bz1=true;
if (bl[b+1]==bl[b]) bz2=false;
else bz2=true;
if (bl[a]==bl[b]){
if (bz1 && bz2) lab[bl[a]]+=c;//lab表示第i个块当前的修改标记
else fo(i,a,b) v[i]+=c;
}
else {
if (bz1 && bz2)
fo(i,bl[a],bl[b]) lab[i]+=c;
else if (bz1 && !bz2) {
fo(i,bl[a],bl[b]-1) lab[i]+=c;//整块打下标记
fo(i,(bl[b]-1)*block+1,b) v[i]+=c;//多余部分直接暴力修改
}
else if (!bz1 && bz2) {
fo(i,bl[a]+1,bl[b]) lab[i]+=c;
fo(i,a,bl[a]*block) v[i]+=c;
}
else {
fo(i,bl[a]+1,bl[b]-1) lab[i]+=c;
fo(i,a,bl[a]*block) v[i]+=c;
fo(i,(bl[b]-1)*block+1,b) v[i]+=c;
}
}
}
实题:hdu1556
题意很明显,就不多说了。
这道题是可以由上述方法解决,涉及到区间修改与单点查询,套上模板即可。
代码:
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define fo(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
const int maxn=1e5+5;
int bl[maxn],a[maxn],lab[350],n,block;
void add(int x,int y)
{
bool bz1,bz2;
if (bl[x]==bl[x-1]) bz1=false; else bz1=true;
if (bl[y]==bl[y+1]) bz2=false; else bz2=true;
if (bl[x]==bl[y]){
if (bz1 && bz2) ++lab[bl[x]];
else fo(i,x,y) ++a[i];
}
else {
if (bz1 && bz2) fo(i,bl[x],bl[y]) ++lab[i];
else if (bz1 && !bz2) {
fo(i,bl[x],bl[y]-1) lab[i]++;
fo(i,(bl[y]-1)*block+1,y) ++a[i];
}
else if (!bz1 && bz2) {
fo(i,bl[x]+1,bl[y]) lab[i]++;
fo(i,x,bl[x]*block) ++a[i];
}
else {
fo(i,bl[x]+1,bl[y]-1) lab[i]++;
fo(i,x,bl[x]*block) ++a[i];
fo(i,(bl[y]-1)*block+1,y) ++a[i];
}
}
}
int main()
{
scanf("%d",&n);
while (n){
block=sqrt(n);
fo(i,1,n) bl[i]=(i-1)/block+1;
bl[0]=1; bl[n+1]=bl[n];
fo(i,1,n){
int x,y;
scanf("%d%d",&x,&y);
add(x,y);
}
fo(i,1,n-1) printf("%d ",a[i]+lab[bl[i]]);//差点被格式坑死- -|||
printf("%d\n",a[n]+lab[bl[n]]);
fo(i,1,n+1){
bl[i]=0;
a[i]=0;
}
scanf("%d",&n);
memset(lab,0,sizeof(lab));
}
}
分块还有很多需要学习,今天只是学习了一点,下次接着学习。