[poj3580]SuperMemo(splay终结题)

版权声明:本文为博主原创文章,转载请附上原博客链接。 https://blog.csdn.net/CABI_ZGX/article/details/82819882

传送门


题意:
你需要维护一组数字,包括这样的几个操作:
给出一个数字序列,有6种操作:
1、 ADD l r d:区间[l,r]的数都加上d。
2、 REVERSE l r : 将区间[l,r]中的数翻转 。
3、 REVOLVE l r t :将区间[l,r]旋转t次,如1 2 3 4 5 旋转2次后就变成4 5 1 2 3 。
4、 INSERT p x :在第p个数后面插入x 。
5、DELETE p :删除第p个数 。
6、 MIN l r : 查询区间[x,y]中的最小值 。


一道不错的splay练手题。
首先用类似线段树的中序遍历建树保证二叉搜索树性质。

对于所有的区间[l,r]操作,可以把l-1旋转到根,r+1旋转到l-1的下面,那么由于二叉搜索树的性质,这时候r+1的左子树就是[l,r]区间。

那么对于区间加法,我们需要维护一个lazy的加法标记,在旋转的时候Pushdown传递标记给左右孩子(与线段树相同),对于区间翻转,由于二叉搜索树的性质,我们在翻转某一棵树的时候只要把他所有的子树的左右孩子翻转一下就好了,那么一样维护一个lazy的旋转标记即可。

对于在p后插入x,我们模仿区间操作的方法,把p和p+1之间的空间“撑”开来。也就是说把p翻转到根,把p+1旋转到p的下面,那么p+1的左子树就空出来了,就可以将新节点插入进去。

对于删除p,我们同样把p-1旋转到根,把p+1旋转到p-1的下面,那么p+1的左子树就是p,直接删掉即可,空间蛮大的这时候最好回收空间。

区间最小值很简单,我们维护一个最小值mn,在Pushup的时候和子树大小size一起被左右孩子更新即可。

接下来剩下的操作就是这题的特殊操作,区间旋转,我们不难发现,因为旋转[l,r]区间t次是往后面推t个数然后放到前面,所以区旋转实际上是交换两个区间。那么我们把旋转[l,r]区间t次可以看做交换[l,r-t]和[r-t+1,r],具体的操作也是区间操作,先把后面被推出去的那段拿出来,放到前面即可。

注意,对于任何涉及区间操作的题目都要设置哨兵节点。


#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
typedef long long ll;
const int N=1e5+10;
const int INF=2147483647;
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}

struct splay
{
	int d,f,size,son[2];
	int fz,mn,add;
}tr[N*2]; int tot,root,n;
void newnode(int x,int d,int f)//新建节点,东西比较多就单放出来了 
{
	tr[x].f=f; tr[x].d=tr[x].mn=d;
	tr[x].size=1; 
	tr[x].fz=tr[x].add=0;
	tr[x].son[0]=tr[x].son[1]=0;
}
void delnode(int x)//删除节点 
{
	tr[x].f=tr[x].d=tr[x].size=tr[x].mn=0;
	tr[x].son[0]=tr[x].son[1]=tr[x].fz=tr[x].add=0;	
}
void update_add(int x,int d)//执行加 
{
	if(!x) return ;
	tr[x].add+=d; tr[x].d+=d; tr[x].mn+=d;
}
void update_fz(int x)//执行翻转 
{
	if(!x) return;
	swap(tr[x].son[0],tr[x].son[1]);
	tr[x].fz^=1;	
}
void pushup(int x)//向上更新size,min 
{
	if(!x) return ;
	int lc=tr[x].son[0],rc=tr[x].son[1];
	tr[x].size=1; tr[x].mn=tr[x].d;
	if(lc) tr[x].size+=tr[lc].size,tr[x].mn=min(tr[x].mn,tr[lc].mn);
	if(rc) tr[x].size+=tr[rc].size,tr[x].mn=min(tr[x].mn,tr[rc].mn);
}
void pushdown(int x)//lazy:更新下方add,fz 
{
	if(!x) return;
	if(tr[x].add)
	{
		update_add(tr[x].son[0],tr[x].add);
		update_add(tr[x].son[1],tr[x].add);
		tr[x].add=0;
	}
	if(tr[x].fz)
	{
		update_fz(tr[x].son[0]);
		update_fz(tr[x].son[1]);
		tr[x].fz=0;	
	}
}
void rotate(int x,int w)
{
	int f=tr[x].f,ff=tr[f].f;
	int r,R;
	
	pushdown(f); 
	pushdown(x);
	
	r=tr[x].son[w],R=f;
	tr[R].son[1-w]=r;
	if(r!=0) tr[r].f=R;
	
	r=x,R=ff;
	if(tr[ff].son[0]==f) tr[R].son[0]=r;
	else tr[R].son[1]=r;
	tr[r].f=R;
	
	r=f,R=x;
	tr[R].son[w]=r;
	tr[r].f=R;
	
	pushup(f);
	pushup(x);
}

void splay(int x,int rt)
{
	pushdown(x);
	while(tr[x].f!=rt)
	{
		int f=tr[x].f,ff=tr[f].f;
		pushdown(ff),pushdown(f),pushdown(x);
		if(ff==rt)
		{
			if(tr[f].son[0]==x) rotate(x,1);
			else rotate(x,0);
		}
		else 
		{
				 if(tr[ff].son[0]==f && tr[f].son[0]==x) rotate(f,1),rotate(x,1);
			else if(tr[ff].son[1]==f && tr[f].son[1]==x) rotate(f,0),rotate(x,0);
			else if(tr[ff].son[0]==f && tr[f].son[1]==x) rotate(x,0),rotate(x,1);
			else if(tr[ff].son[1]==f && tr[f].son[0]==x) rotate(x,1),rotate(x,0);
			//三点一线的情况不能单旋否则复杂度退化 
		}
	}
	pushup(x);
	if(rt==0) root=x;
}
int a[N];
void build(int &x,int l,int r,int f)
{ //中序遍历建树保证平衡 
	if(l>r) return;
	int mid=(l+r)/2;
	x=mid; newnode(x,a[x],f);
	build(tr[x].son[0],l,mid-1,x);
	build(tr[x].son[1],mid+1,r,x);
	pushup(x);
}

int find_kth(int x,int k)
{
    pushdown(x);
    if(tr[tr[x].son[0]].size+1 == k) return x;
    else if(tr[tr[x].son[0]].size >= k) return find_kth(tr[x].son[0],k);
    else return find_kth(tr[x].son[1], k-tr[tr[x].son[0]].size-1);
}

//--------- 
void add(int l,int r,int d) //[l~r]加d 
{
	int x=find_kth(root,l-1),y=find_kth(root,r+1);
	splay(x,0); 
	splay(y,x);
	update_add(tr[y].son[0],d);
}
void ins(int p,int x)//p后面插入x 
{
	int y=find_kth(root,p),z=find_kth(root,p+1);
	splay(y,0); splay(z,y);
	newnode(++tot,x,z);  tr[z].son[0]=tot;
	for(int i=z;i;i=tr[i].f) pushdown(i),pushup(i);
	splay(z,0);
}
void del(int p)//删除p 
{
	int x=find_kth(root,p-1),y=find_kth(root,p+1);
	splay(x,0); splay(y,x);
	delnode(tr[y].son[0]); tr[y].son[0]=0;
	pushup(y); pushup(x);
}
int get_min(int l,int r)//[l,r]间最小值 
{
	int x=find_kth(root,l-1),y=find_kth(root,r+1);
	splay(x,0); splay(y,x);
	return tr[tr[y].son[0]].mn;	
}
void rev(int l,int r)//翻转[l,r] 
{
	int x=find_kth(root,l-1),y=find_kth(root,r+1);
	splay(x,0); splay(y,x);
	update_fz(tr[y].son[0]);	
}
void exchange(int l1,int r1,int l2,int r2)//区间[l1,r1],[l2,r2]交换 
{
	int x=find_kth(root,l2-1),y=find_kth(root,r2+1);
	splay(x,0); splay(y,x); //导出区间
	int tmp=tr[y].son[0]; tr[y].son[0]=0; //剪贴 
	
	x=find_kth(root,l1-1),y=find_kth(root,l1);
	splay(x,0); splay(y,x);
	
	tr[y].son[0]=tmp;
	tr[tmp].f=y;
}
//---

char ss[10];
int main()
{
	scanf("%d",&n);
	a[1]=a[n+1]=INF;//设立哨兵节点 
	for(int i=2;i<=n+1;i++) scanf("%d",&a[i]);
	
	tot=n+2; root=0;
	tr[0].f=tr[0].size=tr[0].son[0]=tr[0].son[1]=tr[0].add=tr[0].fz=0;	
	tr[0].d=INF; 
	build(root,1,n+2,0);
	pushup(root);
	
	int m;scanf("%d",&m);
	int l,r,d; 
	while(m--)//由于有哨兵节点后面的位置都要+1 
	{
		scanf("%s",ss);
		if(ss[0]=='A')
		{
			l=read(),r=read(),d=read();
			add(l+1,r+1,d);
		}
		else if(ss[0]=='I')
		{
			l=read(),d=read();
			ins(l+1,d);	
		}
		else if(ss[0]=='D')
		{
			d=read();
			del(d+1);	
		}
		else if(ss[0]=='M')
		{
			l=read(),r=read();
			printf("%d\n",get_min(l+1,r+1));	
		}
		else if(ss[0]=='R' && ss[3]=='E')
		{
			l=read(),r=read();
			rev(l+1,r+1);	
		}
		else if(ss[0]=='R' && ss[3]=='O')
		{
			l=read(),r=read(),d=read();
			d%=(r-l+1);
			if(d) exchange(l+1,r-d+1,r-d+1+1,r+1);	
		}
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/CABI_ZGX/article/details/82819882
今日推荐