分块:基础与模板
稍暴力的数据结构
操作或查询通常为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号