[NOI2003] 文本编辑器

Posted qjs12

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[NOI2003] 文本编辑器相关的知识,希望对你有一定的参考价值。

  终于知道为什么人人都感慨Splay功能强大了。这道题更像是块状链表的裸题,而且块状链表巨小无比的常数跑起来也非常劲啊。但是我寒假照着板子敲了一遍......敲得我心累啊,敲完以后我就有了我以后再也不会写分块了的念头。但是最近写树套树三道两道被卡常,而看着分块水过一道一道,我心里又慌了......

  说正题。

  首先,对于这道题目,我们的Splay是基于size平衡的,而基于size平衡的Splay百分之九十的情况我们可以用单旋,且因为单旋常数较小,往往有着优秀的表现,编码复杂度直降一个台阶。

  然后题目要求的操作对于功能强大的Splay来说简直小菜一碟,我们只需要提取相应区间按要求做就好了。

  唯一需要特别注意的是对于初始化以及插入操作,而初始化是基于如何插入的。

  对于一般情况来说,考虑在cur后插入len个字符,我们按如下方式实现。

  1:将size==cur的节点x伸展到根。

  2:将size==cur+1的节点y伸展到根。

  上述操作后节点x一定是节点y的左子节点,且y的右子节点为空。

  3:对于y的右子节点我们执行build操作。

  build操作就与一般的build操作相同,按size二分。

  接下来我们考虑初始化。

  初始的cur指向位置0,而在我们递归进行splay的过程中我们使用s[l[x]]+1==k的方式查找size==k的节点,而显然如果cur==0,这个过程会无限进行下去。对于这个问题,我们使cur初始值为1,并建立一个虚拟根节点,这样size[root]==1,size[l[root]]==0,成功解决。

  接下来我们要查找size==cur+1的节点,而现在的splay树中只有一个节点,显然也是无法成功的。所以我们再建立一个节点,建立根节点的左子节点或右子节点都可以,这样我们就可以成功实现两个splay操作,继而实现插入操作了。

  接着,因为我们的空文本的起始位置cur的值定为1,而题目给出的数据对应是0,所以对于MOVE(x)操作,我们总是将x+1的值赋给cur,这样就可以成功对应我们的初始化了。

  说明:我的单旋splay模(mu)板来自于千古神犇MIKE。

 

// q.c

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<cstdlib>
using namespace std;
const int M=2500000+10;
struct SplayTree {
	int root,cnt,cur,l[M],r[M],s[M]; char v[M],c[M];
	SplayTree():root(0),cnt(0),cur(0) {
		memset(l,0,sizeof(l));
		memset(r,0,sizeof(r));
		memset(s,0,sizeof(s));
		memset(v,0,sizeof(v));
		memset(c,0,sizeof(c));
	}
	void reset() { // 初始化.
		root=++cnt,l[root]=++cnt,cur=1;
		s[root]=2,s[l[root]]=1;
	}
	void update(int x) {
		s[x]=s[l[x]]+s[r[x]]+1; 
	}
	void l_rot(int &x) {
		int y=r[x];
		r[x]=l[y],l[y]=x;
		update(x),update(y);
		x=y;
	}
	void r_rot(int &x) {
		int y=l[x];
		l[x]=r[y],r[y]=x;
		update(x),update(y);
		x=y;
	}
	void splay(int &x,int k) {
		int i=s[l[x]]+1;
		if(k==i) return ;
		else if(k<i) splay(l[x],k),r_rot(x);
		else splay(r[x],k-i),l_rot(x);
	}
	void move(int x,bool P) {
		P?cur=x+1:cur+=x;
	}
	void build(int &x,int ll,int rr) {
		if(ll>rr) return ;
		int mid=(ll+rr)>>1;
		if(!x) x=++cnt;
		v[x]=c[mid],s[x]=rr-ll+1;
		build(l[x],ll,mid-1);
		build(r[x],mid+1,rr);
	}
	void insert(int len) {
		splay(root,cur);
		splay(root,cur+1);
		for(int i=1;i<=len;i++) 
			for(c[i]=getchar();c[i]<32||c[i]>126;c[i]=getchar());
		build(r[l[root]],1,len);
		update(l[root]);
		update(root);
	}
	void delet(int len) {
		splay(root,cur);
		splay(root,cur+len+1);
		r[l[root]]=0;
		update(l[root]);
		update(root);
	}
	void prints(int len) {
		splay(root,cur);
		splay(root,cur+len+1);
		dfs(r[l[root]]);
		printf("\n");
	}
	void dfs(int x) {
		if(l[x]) dfs(l[x]);
		printf("%c",v[x]);
		if(r[x]) dfs(r[x]);
	}
}t; 
int main() {
	freopen("editor2003.in","r",stdin);
	freopen("editor2003.out","w",stdout);
	int m,x; char str[17];
	scanf("%d",&m); t.reset();
	for(int i=1;i<=m;i++) {
		scanf("%s",str);
		if(str[0]==‘M‘) scanf("%d",&x),t.move(x,1);
		else if(str[0]==‘I‘) scanf("%d",&x),t.insert(x);
		else if(str[0]==‘D‘) scanf("%d",&x),t.delet(x);
		else if(str[0]==‘G‘) scanf("%d",&x),t.prints(x);
		else if(str[0]==‘P‘) t.move(-1,0);
		else t.move(1,0);
	}
	return 0;
}

 

以上是关于[NOI2003] 文本编辑器的主要内容,如果未能解决你的问题,请参考以下文章

NOI’2003 - Day1 - Problem2 文本编辑器(Editor) 空间

洛谷P4008 [NOI2003]文本编辑器splay

[NOI2003]Editor(块状链表)

1507: [NOI2003]Editor(块状链表)

;~ 小部分AutoHotkey源代码片段测试模板2019年10月9日.ahk

BZOJ1509[NOI2003]逃学的小孩 直径