bzoj1507: [NOI2003]Editor
唉调死我了好端端的splay干嘛非得学块状链表。
好吧简单说说块状链表,这个就是分块的应用嘛。
对于每一个块,用一个数组来维护。
对于块与块之间的维护,则用链表链接。
本来数组定位很快,修改很慢,链表反之,这样弄的话就O(n^2)变成O(n*sqrt(n))了。
大体思路是这样的,然后细节看注释吧。
#include<cstdio> #include<iostream> #include<cstring> #include<algorithm> #include<queue> using namespace std; const int blocksize=20000,blocknum=5000; int n,tp; char str[10001000]; queue<int>q;//里面放可以用的编号,相当于内存回收 struct block { char w[21000]; int len,next; }a[5100]; //内部操作 int ins()//插入 { int x=q.front();q.pop(); return x; } void del(int x)//删除,把整个块的编号扔进队列中 { q.push(x); } void fill(int k,char w[],int len,int next)//对一个块赋值 { a[k].next=next;a[k].len=len; memcpy(a[k].w,w,len);//将w中前len个元素复制到块pos中 } void split(int k,int p)//分裂,使前一个块的长度不超过p { if(a[k].len<=p)return ; int k2=ins(); fill(k2,a[k].w+p,a[k].len-p,a[k].next);//将后面的部分放进下一个块k2 a[k].next=k2;a[k].len=p; } void merge(int k)//合并,将所有的碎块合并,防止出现大量碎块时间退化 { int k2=a[k].next;//下一个块 while(k2!=-1&&a[k].len+a[k2].len<blocksize) { memcpy(a[k].w+a[k].len,a[k2].w,a[k2].len); a[k].len+=a[k2].len;a[k].next=a[k2].next; del(k2);k2=a[k].next; } } int search(int &p)//搜索,返回全局第p个点所属的块,注意是实参,顺便得出在当前这个块是第几个 { int k; for(k=0;k!=-1;k=a[k].next) { if(p<=a[k].len)return k; p-=a[k].len; } return k; } //外部操作 void insert(int p,int L)//插入一段 { //k记录的是当前位置所在块的编号 //先找到p所处的块,然后将k这个块前p个和后面的分出来,这样插入往k这个块后面连就行了 int k=search(p);split(k,p); int i=0; while(i+blocksize<L)//对于插入的一段分块插入 { int k2=ins(); fill(k2,str+i,blocksize,a[k].next); a[k].next=k2;k=k2; i+=blocksize; } //最后一段单独处理 int k2=ins(); fill(k2,str+i,L-i,a[k].next); a[k].next=k2;k=k2; merge(k);//合并维护块 } void dele(int p,int L)//删除一段 { int k=search(p);split(k,p); int i=a[k].next; while(i!=-1&&L>a[i].len) { L-=a[i].len; i=a[i].next; } split(i,L);i=a[i].next; while(a[k].next!=i) { int k2=a[k].next; a[k].next=a[k2].next; del(k2); } merge(k); } void get(int pos,int L)//将需要输出的元素复制到str数组中 { int k=search(pos); int i=min(L,a[k].len-pos); memcpy(str,a[k].w+pos,i); for(k=a[k].next;k!=-1&&i+a[k].len<=L;k=a[k].next) { memcpy(str+i,a[k].w,a[k].len); i+=a[k].len; } if(i<L&&k!=-1)memcpy(str+i,a[k].w,L-i); str[L]=0; } void cs() { for(int i=1;i<=blocknum;i++)q.push(i); a[0].len=0;a[0].next=-1; } char ss[11]; int main() { freopen("editor.in","r",stdin); freopen("editor.out","w",stdout); cs(); int m; scanf("%d",&n); for(int i=1;i<=n;i++) { scanf("%s",ss+1); if(ss[1]=='P')tp--;//前移 else if(ss[1]=='N')tp++;//后移 else if(ss[1]=='M')scanf("%d",&tp);//将光标移动到第K个字符之后 else if(ss[1]=='I')//在光标处插入长度为M的字符串 { scanf("%d",&m); int t=-1; while(t<m-1) { char c=getchar(); if(32<=c&&c<=126)str[++t]=c; } insert(tp,m); } else if(ss[1]=='D')//在光标处删除连续的m个字符 { scanf("%d",&m); dele(tp,m); } else if(ss[1]=='G')//输出光标后连续M个字符 { scanf("%d",&m); get(tp,m);puts(str); } } return 0;
pain and happy in the cruel world.