1. 程式人生 > >分塊 學習筆記

分塊 學習筆記

rec 數組 cst new count tdi 復雜 i++ str

前言:自從發現分塊的暴力分非常之多以後,我就決心學習它,一直拖到現在。

一、簡介:分塊是一種綜合了鏈表與數組優勢的數據結構,可以平衡兩者查詢與修改的復雜度。

二、以普通平衡樹的題目為例,講解一下具體操作

(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

分塊 學習筆記