题意就是要写一个支持一个能插入一堆数,删除一堆数,且可以随机访问的数据结构。
块状链表是很好的工具,大体思路是维护$\sqrt L$个块,块内用数组,块之间用链表连接。
具体做法:
-
维护两个变量$pos1,pos2$表示当前光标在第$pos1$个块的第$pos2$个元素后面
-
对于Insert操作,暴力在$pos2$后面加入这个字符,但如果现在的块长超过了$\sqrt L$(我取了2000),就拆出一个新的块,在链表上修改前驱后继,然后在这个块里面继续插入字符(注意不能改$pos1,pos2$)
-
对于delete操作,从$pos2$后面开始删除,删完了就通过链表找到下一个块,继续。
-
对于get操作,从$pos2$后面开始输出,输完了就通过链表找到下一个块,继续。
-
对于Move操作,从第一个块开始,顺着链表往后走就好了。
-
Prev,Nxt就就从现在的位置往前/往后走就好了,注意如果走到了空的块要跳过
分析一下复杂度:
先分析块的规模:首先每个块长度不超过2000.Insert的字符数量不超过2097152,且次数不超过4000,因此块的数量不超过6000.
Insert操作:总字符不超过2097152,次数不超过4000,没有问题。
delete操作:次数不超过4000,至多删掉所有字符,没有问题;又因为我们对delete的处理不增加新的块,所以块的规模不增大。
get操作:至多输出3M个字符,没有问题。
Move操作:代价不超过块的数量,即6000.
Prev,Nxt代价不超过块的数量,即6000。
因此复杂度没有问题。(因此本题甚至不需要将小的块合并也是可以的)
代码细节较多,要耐心写。
#define MAXN 4000011
#define MAXC 400011
int lim=2000, T,n,len;
int pre[MAXC],nxt[MAXC],pos1,pos2;
std::vector<char>ch[MAXC],tp;
char s[MAXC],a[MAXN];
int newnode(int x)
{
++n;
pre[n]=x,nxt[n]=nxt[x];
pre[nxt[x]]=n,nxt[x]=n;
return n;
}
void Move(int x)
{
++x;
for(int i=1;i;i=nxt[i])
if(ch[i].size()>=x){pos1=i, pos2=x;return;}
else x-=ch[i].size();
puts("???");
}
void Insert()
{
tp.clear();
for(int i=pos2;i<ch[pos1].size();++i)tp.push_back(ch[pos1][i]);
ch[pos1].resize(pos2);
int p=pos1;
for(int i=1;i<=len;++i)
{
if(ch[p].size()>=lim)p=newnode(p);
ch[p].push_back(a[i]);
}
for(char x:tp)
{
if(ch[p].size()>=lim)p=newnode(p);
ch[p].push_back(x);
}
}
void Remove()
{
int p=pos1,it=pos2;
while(len)
{
int rest=int(ch[p].size())-it;
if(rest<=len)ch[p].resize(it),len-=rest;
else
{
for(int i=it+1;i<=it+rest-len;++i)ch[p][i-1]=ch[p][i+len-1];
ch[p].resize(it+rest-len);
len=0;
}
it=0,p=nxt[p];
}
}
void Get(int x)
{
int p=pos1,it=pos2;
while(x)
{
int rest=int(ch[p].size())-it;
if(rest<=x)
{
for(int i=it;i<ch[p].size();++i)putchar(ch[p][i]);
x-=rest;
}
else
{
for(int i=it;i<it+x;++i)putchar(ch[p][i]);
x=0;
}
it=0,p=nxt[p];
}
puts("");
}
void Prev()
{
--pos2;
while(pos2==0)pos1=pre[pos1],pos2=ch[pos1].size();
}
void Nextv()
{
++pos2;
while(pos2>ch[pos1].size())pos1=nxt[pos1],pos2=1;
}
int main()
{
T=read();
pos1=n=1;
ch[pos1].push_back(32),pos2=1;
while(T--)
{
scanf("%s",s+1);
if(s[1]=='M')Move(read());
else if(s[1]=='I')
{
len=read();
for(int i=1;i<=len;++i)
{
char c=getchar();
while(c=='\n'||c=='\r')c=getchar();
a[i]=c;
}
Insert();
}
else if(s[1]=='D'){len=read();Remove();}
else if(s[1]=='G')Get(read());
else if(s[1]=='P')Prev();
else Nextv();
}
return 0;
}
ORZ wh暴切NOI!!!