分块 学习笔记
前言:自从发现分块的暴力分非常之多以后,我就决心学习它,一直拖到现在。
一、简介:分块是一种综合了链表与数组优势的数据结构,可以平衡两者查询与修改的复杂度。
二、以普通平衡树的题目为例,讲解一下具体操作
(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