您的位置:首页 > 其它

[BZOJ1507][NOI2003]Editor(块状链表)

2016-08-24 17:38 381 查看

题目描述

传送门

题解

块状链表模板题,主要能实现几个简单的操作:添加一段区间,删除一段区间,输出一段区间内的元素。

操作的主要过程如下:

插入:找到光标所在的块以及光标在块中的位置,将这个块从这个位置分成两半。将需要插入的区间分成若干个块插入在中间。将碎块合并。

删除:分别找到删除的区间的左右端点所在的块和它们在块中的位置,将这两个块从各自的位置分成两半。将中间的块删除。将碎块合并。

输出:找到光标所在的块和它在块中的位置,向后将一段一段的块拼成给定长度的块。

代码

// BZOJ 1507
#include<iostream>
#include<cstring>
#include<cstdio>
#include<queue>
using namespace std;

const int N=1<<25;
const int blocksize=20000;
const int blocknum=N/blocksize*3;

int T,_;
int cur;
char str[5000005],opt[100];
queue <int> q;
struct node
{
char data[blocksize+111];
int len,nxt;
}a[blocknum+1000];

void init()
{
for (int i=1;i<=blocknum;++i) q.push(i);
a[0].len=0; a[0].nxt=-1;
}
void read(int len)
{
int i=-1;
while (i<len-1)
{
i++;
char c=getchar();
str[i]=c;
if (c<32||c>126) i--;
}
}
//新开一个块的节点
int newnode()
{
int temp=q.front(); q.pop();
return temp;
}
//回收块的节点
void delnode(int t)
{
q.push(t);
}
//找到pos所在的块,并使pos表示在当前块中的位置
void find(int &pos,int &now)
{
for (now=0;a[now].nxt!=-1&&pos>a[now].len;now=a[now].nxt)
pos-=a[now].len;
}
//将新快赋值
void fillnode(int pos,int n,char data[],int nxt)
{
a[pos].nxt=nxt; a[pos].len=n;
memcpy(a[pos].data,data,n);
}
//将块pos在p位置前后分开,变成两个块
void split(int pos,int p)
{
if (a[pos].len==p) return;
int t=newnode();
fillnode(t,a[pos].len-p,a[pos].data+p,a[pos].nxt);
a[pos].nxt=t; a[pos].len=p;
}
//把碎块合并
void maintain(int pos)
{
int t;
for (;pos!=-1;pos=a[pos].nxt)
for (t=a[pos].nxt;t!=-1&&a[pos].len+a[t].len<blocksize;t=a[t].nxt)
{
memcpy(a[pos].data+a[pos].len,a[t].data,a[t].len);
a[pos].len+=a[t].len; a[pos].nxt=a[t].nxt; delnode(t);
}
}
//在光标pos处插入长度为n的str
void insert(int pos,int n)
{
int now,i,t;
//now表示光标所在的块,pos表示光标在这个块中的位置
find(pos,now);
split(now,pos);
for (i=0;i+blocksize<=n;i+=blocksize)
{
t=newnode();
fillnode(t,blocksize,str+i,a[now].nxt);
a[now].nxt=t;
now=t;
}
if (i<n)
{
t=newnode();
fillnode(t,n-i,str+i,a[now].nxt);
a[now].nxt=t;
}
maintain(now);
}
//从光标pos开始删除长度为n的字符串
void del(int pos,int n)
{
int i,now,t;
//now表示光标所在的块,pos表示光标在这个块中的位置
find(pos,now);
split(now,pos);
//找到删除的末尾的点所处的块
for (i=a[now].nxt;i!=-1&&n>a[i].len;i=a[i].nxt)
n-=a[i].len;
split(i,n); i=a[i].nxt;
for (t=a[now].nxt;t!=i;t=a[now].nxt)
a[now].nxt=a[t].nxt,delnode(t);
maintain(now);
}
//从pos这个位置开始输出长度为n的字符串
void get(int pos,int n)
{
int i,now,t;
find(pos,now);
i=min(n,a[now].len-pos);
memcpy(str,a[now].data+pos,i);
for (t=a[now].nxt;t!=-1&&i+a[t].len<=n;t=a[t].nxt)
{
memcpy(str+i,a[t].data,a[t].len);
i+=a[t].len;
}
if (i<n&&t!=-1) memcpy(str+i,a[t].data,n-i);
str
=0;
}
int main()
{
init();
scanf("%d",&T);
while (T--)
{
scanf("%s",opt);
if (opt[0]=='M') scanf("%d",&cur);//改变光标的位置
if (opt[0]=='I')//插入一段区间
{
scanf("%d",&_);
read(_);
insert(cur,_);
}
if (opt[0]=='P') cur--;//光标左移
if (opt[0]=='N') cur++;//光标右移
if (opt[0]=='D')//删除一段区间
{
scanf("%d",&_);
del(cur,_);
}
if (opt[0]=='G')//输出一段区间
{
scanf("%d",&_);
get(cur,_);
puts(str);
}
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: