BZOJ 1507 NOI2003 Editor Splay

本文解析了一道关于字符串编辑操作的模拟题,包括光标移动、插入、删除及获取子串等操作,并提供了详细的实现思路与C++代码。特别注意处理输入字符串中的特殊字符如回车符。

题目大意:

1.将光标移动到某一位置
2.在光标后插入一段字符串
3.删除光标后的一段字符
4.输出光标后的一段字符
5.光标--
6.光标++

和1269很像的一道题,不过弱多了

几个问题需要注意:

1.插入的字符串中间居然会有回车!!没办法了,只能逐个字符进行读入,一旦读到'\n'或者'\r'就重新读入

2.题目描述中说Delete和Get操作后面一定会有足够的字符 纯属放P 连样例都没有足够的字符用来删除 所以删除时要和字符串长度取一个最小值

然后就水水地过去了~

30%达成 今天是不是可以休息了0.0 妈蛋先把圆上的整点搞过去再说

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
struct abcd{
	abcd *fa,*ls,*rs;
	char c;
	int siz;
	bool rev_mark;
	abcd (char C);
	void Push_Up();
}*null=new abcd(0),*root=null;
abcd :: abcd(char C)
{
	fa=ls=rs=null;
	c=C;
	siz=C?1:0;
	rev_mark=0;
}
void abcd :: Push_Up()
{
	siz=ls->siz+rs->siz+1;
}
void Zig(abcd *x)
{
	abcd *y=x->fa;
	y->ls=x->rs;
	x->rs->fa=y;
	x->rs=y;
	x->fa=y->fa;
	if(y==y->fa->ls)
		y->fa->ls=x;
	else if(y==y->fa->rs)
		y->fa->rs=x;
	y->fa=x;
	y->Push_Up();
	if(y==root)
		root=x;
}
void Zag(abcd *x)
{
	abcd *y=x->fa;
	y->rs=x->ls;
	x->ls->fa=y;
	x->ls=y;
	x->fa=y->fa;
	if(y==y->fa->ls)
		y->fa->ls=x;
	else if(y==y->fa->rs)
		y->fa->rs=x;
	y->fa=x;
	y->Push_Up();
	if(y==root)
		root=x;
}
void Splay(abcd *x,abcd *Tar)
{
	while(1)
	{
		abcd *y=x->fa,*z=y->fa;
		if(y==Tar)
			break ;
		if(z==Tar)
		{
			if(x==y->ls)
				Zig(x);
			else
				Zag(x);
			break;
		}
		if(x==y->ls)
		{
			if(y==z->ls)
				Zig(y);
			Zig(x);
		}
		else
		{
			if(y==z->rs)
				Zag(y);
			Zag(x);
		}
	}
	x->Push_Up();
}
void Find(abcd *x,int y,abcd *z)
{
	while(1)
	{
		if(y<=x->ls->siz)
			x=x->ls;
		else
		{
			y-=x->ls->siz;
			if(y==1)
				break;
			y--;
			x=x->rs;
		}
	}
	Splay(x,z);
}
void Output(abcd *x)
{
	if(x==null)
		return ;
	Output(x->ls);
	putchar(x->c);
	Output(x->rs);
}
char s[1<<21];
void Build_Tree(abcd *&x,int l,int r)
{
	if(l>r)
		return ;
	int mid=l+r>>1;
	x=new abcd(s[mid]);
	Build_Tree(x->ls,l,mid-1);
	Build_Tree(x->rs,mid+1,r);
	if(x->ls!=null)
		x->ls->fa=x;
	if(x->rs!=null)
		x->rs->fa=x;
	x->Push_Up();
}
int cursor,m;
int main()
{
	int i,j,x;
	char p[100];
	cin>>m;
	{
		root=new abcd('\n');
		root->rs=new abcd('\n');
		root->rs->fa=root;
		root->Push_Up();
	}
	for(i=1;i<=m;i++)
	{
		scanf("%s",p);
		if(p[0]=='M')
			scanf("%d",&cursor);
		else if(p[0]=='I')
		{
			scanf("%d",&x);
			for(j=0;j<x;j++)
			{
				do s[j]=getchar(); while(s[j]<32);
			}
			Find(root,cursor+1,null);
			Find(root,cursor+2,root);
			Build_Tree(root->rs->ls,0,x-1);
			root->rs->ls->fa=root->rs;
			root->rs->Push_Up();
			root->Push_Up();
		}
		else if(p[0]=='D')
		{
			scanf("%d",&x);
			Find(root,cursor+1,null);
			Find(root,min(cursor+x+2,root->siz),root);
			root->rs->ls=null;
			root->rs->Push_Up();
			root->Push_Up();
		}
		else if(p[0]=='G')
		{
			scanf("%d",&x);
			Find(root,cursor+1,null);
			Find(root,min(cursor+x+2,root->siz),root);
			Output(root->rs->ls);
			puts("");
		}
		else if(p[0]=='P')
			cursor--;
		else
			cursor++;
	}
}


<think>好的,我现在需要详细解释Splay树、Treap树、LCA、线段树和RMO这几个数据结构与算法,并且按照用户的要求用C++代码示例。首先,我需要逐个整理每个概念的基本原理、实现方法和应用场景,然后给出相应的代码片段。同时,还要确保在最后提出相关的问题。 首先,Splay树。它是一种自平衡二叉搜索树,通过splay操作将最近访问的节点移动到根,利用局部性原理提高效率。实现时需要旋转操作,包括zig、zag等。应用场景包括缓存和频繁访问相同数据的场景。我需要写一个Splay树的节点结构,以及splay和插入函数的代码示例。 接下来是Treap树,结合了二叉搜索树和堆的特性。每个节点有优先级,通过旋转维持堆性质。插入时先按BST插入,再按优先级旋转。代码示例应包括节点结构、旋转和插入操作。然后是LCA(最近公共祖先),常见算法有倍增法。预处理每个节点的2^k级祖先,通过二分查找找到LCA。需要实现预处理和查询的函数,代码结构可能包括计算深度、跳转祖先等步骤。 线段树用于区间查询和更新,构建树结构,每个节点代表一个区间。实现时需要建树、查询和更新函数。例如,区间求和的线段树,代码中包括节点结构、递归建树、查询和更新操作。RMO可能是区域管理办公室的缩写,但在数据结构和算法中不常见。可能需要用户确认具体指的是什么。这里假设是范围最小查询(RMQ),所以解释RMQ的稀疏表实现方法,预处理二维数组存储区间最小值,查询时找到区间长度对应的k值,比较两个区间的最小值。 在代码部分,需要注意语法正确性,比如结构体定义、函数参数、递归调用等。例如,Splay树的splay函数需要处理左旋、右旋等情况,而Treap的插入需要维护堆性质,可能涉及左右旋转。最后,相关问题需要围绕这些数据结构的特点、实现细节、应用场景以及相关算法展开。例如,询问Splay树的优势、Treap的优先级作用、LCA的应用、线段树的延迟标记等。</think>以下是C++相关数据结构和算法的详细解释及代码实现: 1. **Splay树(伸展树)** 自平衡二叉搜索树,通过"splay"操作将最近访问的节点移动到根位置 ```cpp struct SplayNode { int key; SplayNode *left, *right, *parent; SplayNode(int k) : key(k), left(nullptr), right(nullptr), parent(nullptr) {} }; void splay(SplayNode* x) { while (x->parent) { if (!x->parent->parent) { // Zig step if (x->parent->left == x) rotateRight(x->parent); else rotateLeft(x->parent); } else if ((x->parent->left == x) && (x->parent->parent->left == x->parent)) { // Zig-zig right rotateRight(x->parent->parent); rotateRight(x->parent); } else if ((x->parent->right == x) && (x->parent->parent->right == x->parent)) { // Zig-zig left rotateLeft(x->parent->parent); rotateLeft(x->parent); } else { // Zig-zag if (x->parent->left == x) { rotateRight(x->parent); rotateLeft(x->parent); } else { rotateLeft(x->parent); rotateRight(x->parent); } } } } ``` 2. **Treap(树堆)** 结合二叉搜索树和堆特性的概率平衡树 ```cpp struct TreapNode { int key, priority; TreapNode *left, *right; TreapNode(int k) : key(k), priority(rand()), left(nullptr), right(nullptr) {} }; TreapNode* rotateRight(TreapNode* y) { TreapNode* x = y->left; y->left = x->right; x->right = y; return x; } TreapNode* insert(TreapNode* root, int key) { if (!root) return new TreapNode(key); if (key < root->key) { root->left = insert(root->left, key); if (root->left->priority > root->priority) root = rotateRight(root); } else { root->right = insert(root->right, key); if (root->right->priority > root->priority) root = rotateLeft(root); } return root; } ``` 3. **LCA(最近公共祖先)** 使用倍增法实现的LCA算法 ```cpp const int MAX = 1000, LOG = 10; int depth[MAX], parent[MAX][LOG]; void preprocess(vector<int> tree[], int u, int p) { parent[u][0] = p; depth[u] = depth[p] + 1; for (int i = 1; i < LOG; ++i) parent[u][i] = parent[parent[u][i-1]][i-1]; for (int v : tree[u]) if (v != p) preprocess(tree, v, u); } int lca(int u, int v) { if (depth[u] < depth[v]) swap(u, v); // 提升较深的节点 for (int i = LOG-1; i >= 0; --i) if (depth[u] - (1<<i) >= depth[v]) u = parent[u][i]; if (u == v) return u; // 同时提升两个节点 for (int i = LOG-1; i >= 0; --i) if (parent[u][i] != parent[v][i]) { u = parent[u][i]; v = parent[v][i]; } return parent[u][0]; } ``` 4. **线段树** 区间查询数据结构示例(区间求和) ```cpp const int MAXN = 1e5; int arr[MAXN], tree[4*MAXN]; void build(int node, int start, int end) { if (start == end) { tree[node] = arr[start]; } else { int mid = (start + end) / 2; build(2*node, start, mid); build(2*node+1, mid+1, end); tree[node] = tree[2*node] + tree[2*node+1]; } } int query(int node, int start, int end, int l, int r) { if (r < start || end < l) return 0; if (l <= start && end <= r) return tree[node]; int mid = (start + end) / 2; return query(2*node, start, mid, l, r) + query(2*node+1, mid+1, end, l, r); } void update(int node, int start, int end, int idx, int val) { if (start == end) { arr[idx] = val; tree[node] = val; } else { int mid = (start + end) / 2; if (start <= idx && idx <= mid) update(2*node, start, mid, idx, val); else update(2*node+1, mid+1, end, idx, val); tree[node] = tree[2*node] + tree[2*node+1]; } } ``` 5. **RMO(Range Minimum Query)** 范围最小查询的稀疏表实现 ```cpp const int MAXN = 1e5, LOG = 17; int st[MAXN][LOG], log_table[MAXN]; void buildRMQ(int arr[], int n) { log_table[1] = 0; for (int i = 2; i <= n; ++i) log_table[i] = log_table[i/2] + 1; for (int i = 0; i < n; ++i) st[i][0] = arr[i]; for (int j = 1; j <= LOG; ++j) for (int i = 0; i + (1 << j) <= n; ++i) st[i][j] = min(st[i][j-1], st[i + (1 << (j-1))][j-1]); } int queryRMQ(int l, int r) { int k = log_table[r - l + 1]; return min(st[l][k], st[r - (1 << k) + 1][k]); } ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值