分块:基础与模板
稍暴力的数据结构
操作或查询通常为4步:
1.判断要操作或是查询的区间是否在一个块内
2.若在一个块内,暴力操作或查询
3.若不在一个块内,将除了最左边和最右边这两个块外其余的块进行整体的操作,即直接对块打上修改标记之类的
4.单独暴力处理最左边的块和最右边的块
入门壹:给出一个长为n的数列,以及n个操作,操作涉及区间加法,单点查值。
#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<cmath>
using namespace std;
int n,a[50005],op,le,ri,c;
int l[50005],r[50005],pos[50005],b[50005],block;
void add(int l,int r,int c){
for(int i=l;i<=min(pos[l]*block,r);i++)
a[i]+=c;
if(pos[l]!=pos[r]){
for(int i=(pos[r]-1)*block+1;i<=r;i++)
a[i]+=c;
}
for(int i=pos[l]+1;i<=pos[r]-1;i++)
b[i]+=c;
}
int main(){
scanf("%d",&n);
block=sqrt(n);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
for(int i=1;i<=n;i++)
pos[i]=(i-1)/block+1;
for(int i=1;i<=n;i++){
scanf("%d%d%d%d",&op,&le,&ri,&c);
if(op==0) add(le,ri,c);
if(op==1) printf("%d\n",a[ri]+b[pos[ri]]);
}
return 0;
}
入门贰:给出一个长为n的数列,以及n个操作,操作涉及区间加法,询问区间内小于某个值x的元素个数。
#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
int n,a[50005],opt,le,ri,c,b[50005];
int l[50005],r[50005],pos[50005],sum[50005];
void reset(int i){
for(int j=l[i];j<=r[i];j++)
b[j]=a[j];
sort(b+l[i],b+r[i]+1);
return ;
}
void add(int left,int right,int c){
if(pos[left]==pos[right]){
for(int i=left;i<=right;i++)
a[i]+=c;
reset(pos[left]);
return ;
}
for(int i=left;i<=r[pos[left]];i++)
a[i]+=c;
reset(pos[left]);
for(int i=l[pos[right]];i<=right;i++)
a[i]+=c;
reset(pos[right]);
for(int i=pos[left]+1;i<pos[right];i++)
sum[i]+=c;
return ;
}
void build(int n){
int block=sqrt(n),tot;
tot=n/block;
if(n%block)
tot++;
for(int i=1;i<=n;i++)
pos[i]=(i-1)/block+1;
for(int i=1;i<=tot;i++){
l[i]=(i-1)*block+1;
r[i]=i*block;
}
r[tot]=n;
for(int i=1;i<=tot;i++)
sort(b+l[i],b+r[i]+1);
return ;
}
int ask(int left,int right,int data){
int ans=0;
if(pos[left]==pos[right]){
for(int i=left;i<=right;i++)
if(a[i]+sum[pos[left]]<data)
ans++;
return ans;
}
for(int i=left;i<=r[pos[left]];i++)
if(a[i]+sum[pos[left]]<data)
ans++;
for(int i=l[pos[right]];i<=right;i++)
if(a[i]+sum[pos[right]]<data)
ans++;
for(int i=pos[left]+1;i<pos[right];i++)
ans+=(lower_bound(b+l[i],b+r[i]+1,data-sum[i])-(b+l[i]));
return ans;
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
b[i]=a[i];
}
build(n);
for(int i=1;i<=n;i++){
scanf("%d%d%d%d",&opt,&le,&ri,&c);
if(!opt) add(le,ri,c);
else printf("%d\n",ask(le,ri,c*c));
}
return 0;
}
入门叁:给出一个长为n的数列,以及n个操作,操作涉及区间加法,询问区间内小于某个值x的前驱(比其小的最大元素)。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
int l[100005],r[100005],pos[100005];
int sum[100005],a[100005],b[100005],n,opt,x,y,c;
void build(int n){
int block=sqrt(n);
int tot=n/block;
if(n%block)
tot++;
for(int i=1;i<=n;i++)
pos[i]=(i-1)/block+1;
for(int i=1;i<=tot;i++){
l[i]=(i-1)*block+1;
r[i]=i*block;
}
r[tot]=n;
for(int i=1;i<=tot;i++)
sort(b+l[i],b+r[i]+1);
}
void reset(int x){
for(int i=l[x];i<=r[x];i++)
b[i]=a[i];
sort(b+l[x],b+r[x]+1);
}
void add(int x,int y,int c){
if(pos[x]==pos[y]){
for(int i=x;i<=y;i++)
a[i]+=c;
reset(pos[x]);
return ;
}
for(int i=x;i<=r[pos[x]];i++)
a[i]+=c;
reset(pos[x]);
for(int i=l[pos[y]];i<=y;i++)
a[i]+=c;
reset(pos[y]);
for(int i=pos[x]+1;i<pos[y];i++)
sum[i]+=c;
}
int ask(int x,int y,int c){
int ans=-1;
if(pos[x]==pos[y]){
for(int i=x;i<=y;i++){
if(a[i]+sum[pos[x]]>=c)
continue;
ans=max(ans,a[i]+sum[pos[x]]);
}
return ans;
}
for(int i=x;i<=r[pos[x]];i++){
if(a[i]+sum[pos[x]]>=c)
continue;
ans=max(ans,a[i]+sum[pos[x]]);
}
for(int i=l[pos[y]];i<=y;i++){
if(a[i]+sum[pos[y]]>=c)
continue;
ans=max(ans,a[i]+sum[pos[y]]);
}
for(int i=pos[x]+1;i<pos[y];i++){
int temp=lower_bound(b+l[i],b+r[i]+1,c-sum[i])-(b+l[i]);
if(temp==0) continue;
ans=max(ans,b[l[i]+temp-1]+sum[i]);
}
return ans;
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
b[i]=a[i];
}
build(n);
for(int i=1;i<=n;i++){
scanf("%d%d%d%d",&opt,&x,&y,&c);
if(opt==0) add(x,y,c);
if(opt==1) printf("%d\n",ask(x,y,c));
}
return 0;
}
入门肆:给出一个长为n的数列,以及n个操作,操作涉及区间加法,区间求和。线段树?
#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
long long l[50005],r[50005],opt,ll,rr,c;
long long pos[50005],block,num,n,a[50005];
long long sum[50005]={0},b[50005]={0};
void build(long long n){
block=sqrt(n);
num=n/block;
if(n%block)
num++;
for(long long i=1;i<=num;i++){
l[i]=(i-1)*block+1;
r[i]=i*block;
}
r[num]=n;
for(long long i=1;i<=n;i++){
pos[i]=(i-1)/block+1;
sum[pos[i]]+=a[i];
}
}
void add(long long ll,long long rr,long long c){
if(pos[ll]==pos[rr]){
for(long long i=ll;i<=rr;i++)
a[i]+=c;
sum[pos[ll]]+=(rr-ll+1)*c;
return;
}
for(long long i=ll;i<=r[pos[ll]];i++){
a[i]+=c;
sum[pos[i]]+=c;
}
for(long long i=l[pos[rr]];i<=rr;i++){
a[i]+=c;
sum[pos[i]]+=c;
}
for (long long i=pos[ll]+1;i<pos[rr];i++)
b[i]+=c;
}
void ask(long long ll,long long rr,long long c){
long long ans=0;
if(pos[ll]==pos[rr]){
for(long long i=ll;i<=rr;i++)
ans+=(b[pos[ll]]+a[i]);
printf("%lld\n",ans%(c+1));
return ;
}
for(long long i=ll;i<=r[pos[ll]];i++)
ans+=(a[i]+b[pos[ll]]);
for(long long i=l[pos[rr]];i<=rr;i++)
ans+=(a[i]+b[pos[rr]]);
for(long long i=pos[ll]+1;i<pos[rr];i++)
ans+=(sum[i]+b[i]*block);
printf("%lld\n",ans%(c+1));
}
int main(){
scanf("%lld",&n);
for(long long i=1;i<=n;i++)
scanf("%lld",&a[i]);
build(n);
for(long long i=1;i<=n;i++){
scanf("%lld%lld%lld%lld",&opt,&ll,&rr,&c);
if(!opt) add(ll,rr,c);
else ask(ll,rr,c);
}
return 0;
}

浙公网安备 33010602011771号