【分块】枚举(enum)-链表

题目来源:《高级数据结构》


题意

    有一列整数,共n个。每次可以对这些整数有两种操作:
    (1)第i个到第j个整数分别加上数p;
    (2)询问这些数中比t小的数的个数。


输入

第一行有两个数,n和m(1≤n≤100000,m≤10000),表示数的个数和操作数。
第二行n个整数,表示每个数的初始值。
以后m行,每行开始一个数q。若q为1,则后面跟三个数:i和j(i≤j)表示两个下标,p(-1000≤p≤1000)表示修改的数;若q为2,则为询问操作,后面跟一个数t。


输出

对每个询问操作输出数列中比t小的数的个数。


题解

    首先说明,大佬们看到这两种操作,很快就会想到线段树和平衡树,所以这道题也可以用树套树做。
    可是本蒟蒻不会啊,无奈又找不到题解,于是在弃疗一周以后再次翻出来打,居然AC了!用的是非常淳朴的分块暴力。链表非常zz,其实根本都可以不用,因为没有插入和删除的动作(而这道题中每个数的值与标号是无关的,而我们的判断肯定会先分块排好序,所以如果要分链表,是无法实现的)。
    这道题我们可以按标号升序建立 n n 个块,在每个块中把每个数升序排序(这个操作我是用归并写的)。
    对于修改操作,如果覆盖了整个块,我们直接加lazy标记,如果是一部分,遍历整个块,找出对应的数修改,再重新排序(是的,你没有看错,就是这么暴力).
    对于查询操作,同上,若覆盖整个块,就把答案加上这个块的大小,若是一部分,我们就用lower_bound找到第一个大于等于t的数的位置,因为是升序排,然后直接加就好了。
    等本蒟蒻学会了树套树,就来补代码!


代码

#include
#include
#include
#include
#include
#include
#include
#define zz 300//每个块的大小
using namespace std;
const int N=1e5+10;
const int M=2e4+10;
int n,m,cnt;
int v[N];//把每个位置上的值单独存一个数组以便lower_bound查找

struct Node{
    int val;
    int num;
}a[N],qw[N];

struct P{
    int fr,to;
    int siz,tag;
    int l,r;
}t[N];

inline int read()
{
    char c=getchar();int x=0,t=1;
    while(c<'0' || c>'9') {if(c=='-') t=-1;c=getchar();}
    while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
    return x*t;
}

inline void merge(int L,int R,int mid)
{
    int i=L,j=mid+1,k=0;
    while(i<=mid && j<=R){
        if(a[i].val>a[j].val){
            qw[k++]=a[j++];
        }else{
            qw[k++]=a[i++];
        }
    }
    while(i<=mid) qw[k++]=a[i++];
    while(j<=R) qw[k++]=a[j++];
    for(int i=0;iqw[i];
}

inline void sor(int L,int R)
{
    int mid=(L+R)>>1;
    if(L1,R);
        merge(L,R,mid);
    }
}

inline void pre()
{
    int ty=n;
    while(ty>0){
        if(ty>zz){
            t[++cnt].l=n-ty+1;
            t[cnt].r=n-ty+zz;
            t[cnt].fr=cnt-1;
            t[cnt].to=cnt+1;
        }else{
            t[++cnt].l=n-ty+1;
            t[cnt].r=n;
            t[cnt].fr=cnt-1;
        }
        t[cnt].siz=t[cnt].r-t[cnt].l+1;
        sor(t[cnt].l,t[cnt].r);
        for(int i=t[cnt].l;i<=t[cnt].r;i++)
          v[i]=a[i].val;
        ty-=zz;
    }
}

inline int query(int q)
{
    int p=1,ans=0;
    while(p!=0){
        if(v[t[p].l]+t[p].tag >=q){
            p=t[p].to;continue;
        }
        if(v[t[p].r]+t[p].tag<q){
            ans+=t[p].siz;
            p=t[p].to;continue;
        }
        int f=lower_bound(v+t[p].l,v+1+t[p].r,q-t[p].tag)-v;
        ans+=f-t[p].l;
        p=t[p].to;
    }
    return ans;
}

inline void change(int L,int R,int ad)
{
    int p=1;
    while(t[p].rif(t[p].lfor(int i=t[p].l;i<=t[p].r;i++){
            if(a[i].num>=L && a[i].num<=R){
                a[i].val+=ad;
            }
        }
        sor(t[p].l,t[p].r);
        for(int i=t[p].l;i<=t[p].r;i++)
          v[i]=a[i].val;
        p=t[p].to;
    }
    while(t[p].l>=L && t[p].r<=R){t[p].tag+=ad;p=t[p].to;}
    if(t[p].l<=R){
        for(int i=t[p].l;i<=t[p].r;i++){
            if(a[i].num>=L && a[i].num<=R){
                a[i].val+=ad;
            }
        }
        sor(t[p].l,t[p].r);
        for(int i=t[p].l;i<=t[p].r;i++)
          v[i]=a[i].val;
    }
}

int main(){
    n=read();m=read();
    for(int i=1;i<=n;i++) a[i].val=read(),a[i].num=i;
    pre();
    while(m--){
        int op=read();
        if(op==1){
            int i=read(),j=read(),ds=read();
            change(i,j,ds);
        }else{
            printf("%d\n",query(read()));
        }
    }
    return 0;
}

你可能感兴趣的:(【分块】枚举(enum)-链表)