1 条题解
-
0
自动搬运
来自洛谷,原作者为

HenryHuang
单程孤舟,出云入霞,如歌如吟。搬运于
2025-08-24 21:55:30,当前版本为作者最后更新于2019-05-02 21:18:24,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
求管理员让过
先推广一下
MY BLOG
我们考虑这样的一个问题
给你一个序列,要求你支持插入,删除,查询单点值
如果用数组,查询O(1),插入删除最坏O(n)
如果用链表,插入删除O(1),查询最坏O(n)
如果用平衡树……
不要跟我说平衡树那么我们是否可以考虑:将一个一个的数组以链表的形式串起来,这样是否会提高操作的效率,又是否会降低一些操作的效率呢?

可以手动模拟一下各种操作
块状链表就是这样一个略显暴力的算法
但其复杂度较为优秀,所以在很多地方的应用都非常广
用一句话说叫“弱弱联合”
码量
稍大,但极易理解,打着打着就打出两百K行先介绍一下比较基本的操作吧
Spilt
当一个块的长度过大,我们就可以考虑将其分裂成两个较小的块。
在处理类似于插入或者删除这类操作时,我们可以先从当前位置将其分裂成两个块,这样就可以十分方便的进行操作了。

Merge
同理,就是的逆运算。
部分Maintain
看到很多代码对于每一次操作都遍历一遍整个链表,其实大可不必。
姑且称其为部分maintain吧,我也不知道叫什么。
在进行操作时,我们可能会使得一些块过大,一些块过小。
所以我们需要通过或者来调整。
我们发现,在进行操作时所需要考虑的需要维护的块:区间前的那一块与区间开头块;区间末尾块与区间后的那一块。
这样做可能会使得块状链表没有在经过完整maintain操作时平衡,但会大大减少维护时的常数,而平衡程度也可以接受。
一般采用的维护方法:保证相邻两块大小加起来大于,但每块大小不超过,这样可以较好的维护平衡,同时不用考虑当块较大时的操作,可以使块的数量控制在
这是作者经过权衡后得出的做法,实测复杂度优秀,复杂度为。
然后,我们切入正题。
Insert
查找光标块内的位置,在此位置将块分裂,然后将字符串一块一块地插入
Delete
同理
Get
不许要分裂,直接利用函数,对其进行复制粘贴即可
代码中有较详细注释,贴代码
#include<bits/stdc++.h> using namespace std; char xch,xB[1<<15],*xS=xB,*xTT=xB; #define getc() (xS==xTT&&(xTT=(xS=xB)+fread(xB,1,1<<15,stdin),xS==xTT)?0:*xS++) inline int read() { int x=0,f=1;char ch=getc(); while(ch<'0'|ch>'9'){if(ch=='-')f=-1;ch=getc();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();} return x*f; }//为了使程序跑得更快所使用的读入优化 const int maxn=2e3+10; struct node{ int nex,siz;//每一块数组的后继以及大小 char a[maxn<<1]; }b[maxn<<2]; int pool[maxn<<2],cnt,curpos;//内存池、指针以及当前光标位置 inline int modi(){return pool[cnt++];}//内存分配 inline void dele(int x){pool[--cnt]=x;}//内存回收 inline void init() { for(int i=1;i<(maxn<<2);++i) pool[i]=i;//维护内存池,动态分配回收内存 cnt=1; b[0].siz=0,b[0].nex=-1;//新建一个0号节点,方便操作 } inline void add(int x,int y,int num,char c[])//在第x块后添加一个编号为y的块,长度为num { if(y!=-1) { b[y].nex=b[x].nex,b[y].siz=num; memcpy(b[y].a,c,num); } b[x].nex=y; } inline void merge(int x,int y)//将第x块和第y块合并 { memcpy(b[x].a+b[x].siz,b[y].a,b[y].siz); b[x].siz+=b[y].siz,b[x].nex=b[y].nex; dele(y); } inline void split(int cur,int pos)//将第cur块从pos处分割 { if(cur==-1||pos==b[cur].siz) return ; add(cur,modi(),b[cur].siz-pos,b[cur].a+pos); b[cur].siz=pos; } inline int pos(int &x)//寻找当前光标所在的块和块内位置 { int now=0; while(now!=-1&&x>b[now].siz) x-=b[now].siz,now=b[now].nex; return now; } inline void insert(int p,int num,char c[])//在p位置之后插入长度为num的字符串 { int now=pos(p); split(now,p); int tot=0,nb,st=now; while(tot+maxn<=num)//维护块状链表平衡 { nb=modi(); add(now,nb,maxn,c+tot); tot+=maxn; now=nb; } if(num-tot) nb=modi(),add(now,nb,num-tot,c+tot); if(b[now].siz+b[nb].siz<maxn&&nb!=-1)//不用对整个链表进行判断,部分maintain merge(now,nb),nb=b[now].nex; if(b[st].siz+b[b[st].nex].siz<maxn&&b[st].nex!=-1)//同理 merge(st,b[st].nex); // maintain(); } inline void erase(int p,int num)//在p位置之后删除长度为num的字符串 { int now=pos(p); split(now,p); int nex=b[now].nex; while(nex!=-1&&num>b[nex].siz) num-=b[nex].siz,nex=b[nex].nex; split(nex,num); nex=b[nex].nex; for(int i=b[now].nex;i!=nex;i=b[now].nex) b[now].nex=b[i].nex,dele(i); while(b[now].siz+b[nex].siz<maxn&&nex!=-1)//不用对整个链表进行判断,部分maintain merge(now,nex),nex=b[now].nex; // maintain(); } char ans[20000000]; inline void get(int p,int num)//输出p位置后长度为num的字符串 { int cur=pos(p); int tot=b[cur].siz-p; if(num<tot) tot=num; memcpy(ans,b[cur].a+p,tot); int now=b[cur].nex; while(now!=-1&&num>=tot+b[now].siz) { memcpy(ans+tot,b[now].a,b[now].siz); tot+=b[now].siz,now=b[now].nex; } if(num-tot>0&&now!=-1) memcpy(ans+tot,b[now].a,num-tot); ans[num]='\0';//为了不清空,用\0结束 printf("%s\n",ans); } inline char opt() { char c=getc(); while(c!='M'&&c!='I'&&c!='D'&&c!='G'&&c!='P'&&c!='N') c=getc(); return c; }//为了不与读入优化冲突 int main() { // freopen("3.in","r",stdin); // freopen("3.ans","w",stdout); init(); int m; scanf("%d",&m); for(int i=1;i<=m;++i) { switch(opt()) { case 'M':curpos=read();break; case 'I': int tmp; tmp=read(); for(int i=0;i<tmp;++i) { ans[i]=getc(); if(ans[i]<32||ans[i]>126) --i; } insert(curpos,tmp,ans); break; case 'D': tmp=read(); erase(curpos,tmp); break; case 'G': tmp=read(); get(curpos,tmp); break; case 'P':--curpos;break; case 'N':++curpos;break; } } return 0; }
- 1
信息
- ID
- 2960
- 时间
- 1000ms
- 内存
- 125MiB
- 难度
- 6
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者