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;

posted @ 2017-11-21 13:57  AKCqhzdy  阅读(239)  评论(0编辑  收藏  举报