「分块」数列分块入门2 by hzwer

题意:给出一个长为n的数列,以及n个操作,操作涉及区间加法,询问区间内小于某个值x的元素个数。

注意细节!! 对于最后一块非整块,要特殊处理!!!

/*
loj6278
*/
#include<bits/stdc++.h>
using namespace std;
#define N 50005
int pos[N],sta[505],end[505],a[N],fl[505],b[N],block,n;//a是原数组 b是每一个块内有序的数组 
void modify(int l,int r,int del)
{
    int p=pos[l],q=pos[r];
    if(p==q){
        for(int i=sta[p];i<=end[p];i++){
            a[i]+=fl[p];//注意加上原来已经有的区间标记 
            if(i>=l&&i<=r) a[i]+=del;
            b[i]=a[i];
        } 
        fl[p]=0; sort(b+sta[p],b+end[p]+1); return ;
    }
    //先处理零散的部分 暴力加后对整个块排序 记得要+1 一定要记得将块的标记下传!! 
    for(int i=sta[p];i<=end[p];i++){
        a[i]+=fl[p];
        if(i>=l) a[i]+=del;
        b[i]=a[i];
    } 
    fl[p]=0;//记得将标记置0 
    sort(b+sta[p],b+end[p]+1);
    for(int i=sta[q];i<=end[q];i++){
        a[i]+=fl[q];
        if(i<=r) a[i]+=del;
        b[i]=a[i];
    } 
    fl[q]=0;
    sort(b+sta[q],b+end[q]+1);
    //对于整块 直接打标记 
    for(int i=p+1;i<=q-1;i++) fl[i]+=del;
}
int query(int l,int r,int x)
{
    int p=pos[l],q=pos[r],ans=0;
    if(p==q){
        for(int i=l;i<=r;i++) ans+=(a[i]+fl[p]<x); return ans;
    }
    for(int i=l;i<=end[p];i++) ans+=(a[i]+fl[p]<x);//要加上原有的区间标记 
    for(int i=sta[q];i<=r;i++) ans+=(a[i]+fl[q]<x);
    for(int i=p+1;i<=q-1;i++) ans+=lower_bound(b+sta[i],b+end[i]+1,x-fl[i])-b-sta[i];//二分和sort尾结点都要+1!! 
    return ans;
}
int main()
{
    int op,aa,bb,c;
    scanf("%d",&n);

    block=(int) sqrt(n);
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]); b[i]=a[i];
        pos[i]=(i+block-1)/block;
        if(!sta[pos[i]]) sta[pos[i]]=i;
        end[pos[i]]=i;
    }
    for(int i=1;i<=pos[n];i++){
        int x=block*(i-1)+1,y=min(block*i,n);//一定要注意边界!!! 
        sort(b+x,b+y+1);
    }
    int t=n;
    while(t--){
        scanf("%d%d%d%d",&op,&aa,&bb,&c);
        if(op==0) modify(aa,bb,c);
        else printf("%d
",query(aa,bb,c*c));
    }
}
/*
4
3 2 2 1 
0 1 3 1
1 1 3 2
1 1 4 1
1 2 3 2
ans 2 0 2
9
6 4 7 5 3 1 9 5 5
1 2 7 2
*/
原文地址:https://www.cnblogs.com/mowanying/p/11240203.html