分块:把一个长度为n的数列a[]分成sqrt(n)块,每块长度为bl=sqrt(n),用b[i]纪录第i个数a[i]属于第几块,b[i]=(i-1)/bl+1;
操作或查询通常为 4 步:
1.判断要操作或是查询的区间是否在一个块内
2.若在一个块内,暴力操作或查询
3.若不在一个块内,将除了最左边和最右边这两个块外其余的块进行整体的操作,即直接对块打上修改标记之类的
4.单独暴力处理最左边的块和最右边的
复杂度:n*√n,查询o(1);
基础题1:长度为n的数列,区间修改,单点查询
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+50;
int n,m,opt,bl,l,r,k;
int a[N],b[N],fl[N];//fl为块的值,类似懒标记
void add(int l,int r,int k)
{
for(int i=l;i<=min(r,b[l]*bl);i++)//左边不完整的
a[i]+=k;
for(int i=b[l]+1;i<=b[r]-1;i++)//完整块
fl[i]+=k;
if(b[l]!=b[r])
for(int i=(b[r]-1)*bl+1;i<=r;i++)//右边不完整块
a[i]+=k;
}
int main()
{
scanf("%d",&n);
bl=sqrt(n);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
b[i]=(i-1)/bl+1;
}
for(int i=1;i<=n;i++)
{
scanf("%d %d %d %d",&opt,&l,&r,&k);
if(opt==0)
add(l,r,k);
else
printf("%d\n",a[r]+fl[b[r]]);
}
return 0;
}
基础题2:给出一个长为n的数列,以及n个操作,操作涉及区间加法,询问区间内小于某个值x的元素个数。
题目传送门
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+50;
int n,m,opt,bl,l,r,k;
int a[N],b[N],fl[N];
vector<int >g[505];
void reset(int s)
{
int j=b[s];
g[j].clear();
for(int i=(j-1)*bl+1;i<=min(n,j*bl);i++)
g[j].push_back(a[i]);
sort(g[j].begin(),g[j].end());
}
void add(int l,int r,int k)
{
for(int i=l;i<=min(r,b[l]*bl);i++)
a[i]+=k;
reset(l);
for(int i=b[l]+1;i<=b[r]-1;i++)
fl[i]+=k;
if(b[l]!=b[r])
for(int i=(b[r]-1)*bl+1;i<=r;i++)
a[i]+=k;
reset(r);
}
int query(int l,int r,int c)
{
int ans=0;
for(int i=l;i<=min(r,b[l]*bl);i++)
if(a[i]+fl[b[i]]<c*c)ans++;
for(int i=b[l]+1;i<=b[r]-1;i++){
int t=lower_bound(g[i].begin(),g[i].end(),c*c-fl[i])-g[i].begin();
ans+=t;
}
if(b[l]!=b[r])
for(int i=(b[r]-1)*bl+1;i<=r;i++)
if(a[i]+fl[b[i]]<c*c)ans++;
return ans;
}
int main()
{
scanf("%d",&n);
bl=sqrt(n);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
b[i]=(i-1)/bl+1;
g[b[i]].push_back(a[i]);
}
int num=(n-1)/bl+1;
for(int i=1;i<=num;i++)
{
sort(g[i].begin(),g[i].end());
}
for(int i=1;i<=n;i++)
{
scanf("%d %d %d %d",&opt,&l,&r,&k);
if(opt==0)
add(l,r,k);
else
printf("%d\n",query(l,r,k));
}
return 0;
}