分块 学习笔记

前言:自从发现分块的暴力分非常之多以后,我就决心学习它,一直拖到现在。

一、简介:分块是一种综合了链表与数组优势的数据结构,可以平衡两者查询与修改的复杂度。

二、以普通平衡树的题目为例,讲解一下具体操作

(0)我们对每一块维护如下的域

\(nxt:\)下一块的编号
\(siz:\)当前块的大小
\(dat[]:\)当前块的元素

在此题中保证块外快内的元素同时都是有序的

(1)\(merge(x)\)

作用:将\(x\)与它的下一块合并

操作:将下一块的元素接在当前块的后面,更新当前块,删除下一块

Code:

void Merge(int cur)//合并它和它后面的
{
    memcpy(t[cur].dat+t[cur].siz,t[Next].dat,t[Next].siz<<2);
    Size+=t[Next].siz;
    del(Next);
    Next=t[Next].nxt;
}

附注:有

#define Next t[cur].nxt
#define Size t[cur].siz

关于\(memcpy(void *a,void *b,int \ count)\)

代表把数组\(b\)\(count\)字节接到到\(a\)数组传进来的位置后

(2)\(maintain()\)

作用:维护块的大小

操作:扫描,将较小的两个块合并

Code:

void maintain()
{
    int cur=head;
    while(Next)
    {
        while(Next&&Size+t[cur].siz<=S) Merge(cur);
        cur=Next;
    }
}

附注:\(S\)大概是\(\sqrt n\)

(3)\(split(cur,pos)\)

作用:将\(cur\)这个块分成两个,\(pos\)归前面

操作:新建块,处理信息

Code:

void split(int cur,int pos)
{
    if(pos==Size-1) return;
    int newnode=New();
    memcpy(t[newnode].dat,t[cur].dat+pos+1,Size-pos-1<<2);
    t[newnode].siz=Size-pos-1;Size=pos+1;
    t[newnode].nxt=Next;
    Next=newnode;
}

以下的操作基本可以按自己喜好来了,我介绍一下自己的

(4)\(Insert(x)\)

作用:插入\(x\)

操作:先分裂,然后搞新块(避免块过大),最后\(maintain\)一下

Code:

void Insert(int k)
{
    int cur,pos;
    pre(cur,pos,k);
    split(cur,pos);
    int newnode=New();
    t[newnode].dat[0]=k,t[newnode].siz=1;
    t[newnode].nxt=Next,Next=newnode;
    maintain();
}

(5)\(extrack(x)\)

作用:删除\(x\)

操作:先分裂,然后直接删,在块头的话特判一下

Code:

void extrack(int k)
{
    int cur,pos,curn,posn;
    pre(curn,posn,k);
    cur=curn,pos=posn;
    getnext(cur,pos);
    split(cur,pos);
    if(!pos) {t[curn].nxt=Next,del(cur);return;}
    --Size;
    maintain();
}

(6)\(pre(\&cur,\&pos,k)\)

作用:就是求前驱

操作:遍历

Code:

void pre(int &cur,int &pos,int k)
{
    cur=head,pos=0;
    while(Next&&t[Next].dat[0]<k) cur=Next;
    while(pos+1<Size&&t[cur].dat[pos+1]<k) ++pos;
}

(7)\(getnext(\&cur,\&pos)\)

作用:找下一个元素

操作:模拟。

Code:

void getnext(int &cur,int &pos)
{
    if(pos<Size-1) {++pos;return;}
    pos=0;
    if(Next) {cur=Next;return;}
    Next=0;
}

三、参考代码

Code:

#include <cstdio>
#include <cstring>
#define Next t[cur].nxt
#define Size t[cur].siz
const int N=1e5+10;
const int inf=0x7fffffff;
const int S=4e2;
struct node
{
    int nxt,siz,dat[S<<1];
}t[S<<1];
int q[N<<1],l=1,r,tot,head,n;
int New()
{
    if(l<=r) return q[l++];
    else return ++tot;
}
void del(int cur)
{
    q[++r]=cur;
}
void Merge(int cur)//合并它和它后面的
{
    memcpy(t[cur].dat+t[cur].siz,t[Next].dat,t[Next].siz<<2);
    Size+=t[Next].siz;
    del(Next);
    Next=t[Next].nxt;
}
void maintain()
{
    int cur=head;
    while(Next)
    {
        while(Next&&Size+t[cur].siz<=S) Merge(cur);
        cur=Next;
    }
}
void split(int cur,int pos)
{
    if(pos==Size-1) return;
    int newnode=New();
    memcpy(t[newnode].dat,t[cur].dat+pos+1,Size-pos-1<<2);
    t[newnode].siz=Size-pos-1;Size=pos+1;
    t[newnode].nxt=Next;
    Next=newnode;
}
void pre(int &cur,int &pos,int k)
{
    cur=head,pos=0;
    while(Next&&t[Next].dat[0]<k) cur=Next;
    while(pos+1<Size&&t[cur].dat[pos+1]<k) ++pos;
}
void getnext(int &cur,int &pos)
{
    if(pos<Size-1) {++pos;return;}
    pos=0;
    if(Next) {cur=Next;return;}
    Next=0;
}
void suc(int &cur,int &pos,int k)
{
    pre(cur,pos,k);
    while(t[cur].dat[pos]<=k) getnext(cur,pos);
}
void Insert(int k)
{
    int cur,pos;
    pre(cur,pos,k);
    split(cur,pos);
    int newnode=New();
    t[newnode].dat[0]=k,t[newnode].siz=1;
    t[newnode].nxt=Next,Next=newnode;
    maintain();
}
void extrack(int k)
{
    int cur,pos,curn,posn;
    pre(curn,posn,k);
    cur=curn,pos=posn;
    getnext(cur,pos);
    split(cur,pos);
    if(!pos) {t[curn].nxt=Next,del(cur);return;}
    --Size;
    maintain();
}
int Rank(int k)
{
    int cur=head,pos=0,sum=0;
    while(t[cur].dat[Size-1]<k) sum+=Size,cur=Next;
    while(t[cur].dat[pos]<k) ++pos;
    sum+=pos;
    return sum;
}
int fRank(int k)
{
    int cur=head;
    while(Size<k) k-=Size,cur=Next;
    return t[cur].dat[k-1];
}
int main()
{
    freopen("data.in","r",stdin);
    freopen("data.out","w",stdout);
    scanf("%d",&n);
    head=++tot;
    t[head].dat[0]=-inf,t[head].siz=1;
    for(int op,x,cur,pos,i=1;i<=n;i++)
    {
        scanf("%d%d",&op,&x);
        if(op==1) Insert(x);
        else if(op==2) extrack(x);
        else if(op==3) printf("%d\n",Rank(x));
        else if(op==4) printf("%d\n",fRank(++x));
        else if(op==5)
        {
            pre(cur,pos,x);
            printf("%d\n",t[cur].dat[pos]);
        }
        else
        {
            suc(cur,pos,x);
            printf("%d\n",t[cur].dat[pos]);
        }
    }
    return 0;
}


2018.8.25

posted @ 2018-08-25 10:07  露迭月  阅读(153)  评论(0编辑  收藏  举报