重新写了一下splay,并且琢磨自己的版。
以前看过这道题,感觉操作好繁琐,不做。
今天看了一下,恩,有必要再写一下splay,先拿这道题练手。
题意:
给定序列,维护6个操作:
ADD l r x 把[l,r]加上x
REVERSE l r 把[l,r]翻转
REVOLVE l r t 把[l,r]这一段向后滚动t次, 比如1,2,3 滚动2次变成 2,3,1
INSERT x y 在x后面插入y
DELTEL x删掉第x个元素
MIN l r 询问[l,r]中的最小值
对于第一个操作,用lazy标记维护pushdown一下就好了
对于第二个操作,用bool 型的rev维护pushdown一下就好了
第三个操作,先将t对区间长度取mod,然后分成前面的区间和后t个元素的区间,交换这两个区间的先后,我们可以先把两个区间分别翻转,再把整个区间翻转,这样影响的只是前后两个区间的位置,不影响里面的元素排列。
第四个操作,把x旋到根,x+1旋到根的右儿子,root->r->l一定为空,直接插入元素即可
第五个操作,把x-1旋到根,x+1旋到根的右儿子,root->r-l就是x,删掉就好了
第6个,在splay维护一个最小值,每次pushdown和rotate的时候更新一下,到时候照样的l-1旋到根,r+1选到根的右边,查询root->r-l即可。
PS:注意如果splay单纯地维护[1,n],对于l-1,r+1可能会越界,需要特判.
但是特判是在是麻烦,代码量增长不少,所以为了简便,我提前插入了0和n+1两个节点,之后涉及到区间的都把区间标号+1
然后就没有了,我debug还是调了那么久,毕竟数据结构的题嘛,不过要比我想象的顺利一些。
PS被坑点:
1.DEL之后将空节点选到了根(果断RE,都空了,咋旋)
2.REVOLVE那里开始写的特别复杂,后来改成这样,不过把又把区间标号向右移了1,算上翻转那里又+1,就把区间标号向右移了2次。
splay的性质是,把操作过的点旋到根,这样在一种玄学的推理下单词查询复杂度降为均摊logn . 所以我把insert和min涉及到的点旋到了根,应该会快些吧- -
然后就附上代码:
#include<cstdio>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<queue>
#include<cmath>
using namespace std;
const int maxn=100000+20;
struct node
{
node *f;
node *ch[2];
int sz,key,m;//size key min
int lazy;//addd
bool rev;//reverse
void rs()
{
sz=1+ch[0]->sz+ch[1]->sz;
m=key;
m=min(m,ch[0]->m);
m=min(m,ch[1]->m);
}
}S[maxn<<2],*root,*null;
int ncnt;
int n,m;
void clear()
{
ncnt=0;
null=&S[ncnt];
null->sz=0;
null->key=0x3f3f3f3f;
null->m=0x3f3f3f3f;
null->lazy=null->rev=0;
root=null;
}
node *newnode(int key)
{
node *p=&S[++ncnt];
p->sz=1;
p->key=p->m=key;
p->lazy=p->rev=0;
p->ch[0]=p->ch[1]=p->f=null;
return p;
}
void pushdown(node *u)
{
if(u==null)return ;
if(u->lazy)
{
if(u->ch[0]!=null)
{
u->ch[0]->m+=u->lazy;
u->ch[0]->key+=u->lazy;
u->ch[0]->lazy+=u->lazy;
}
if(u->ch[1]!=null)
{
u->ch[1]->m+=u->lazy;
u->ch[1]->key+=u->lazy;
u->ch[1]->lazy+=u->lazy;
}
u->lazy=0;
}
if(u->rev)
{
swap(u->ch[0],u->ch[1]);
if(u->ch[0]!=null)u->ch[0]->rev^=1;
if(u->ch[1]!=null)u->ch[1]->rev^=1;
u->rev^=1;
}
}
void rotate(node *u)
{
node *f=u->f;
if(f==null)return ;
node *ff=f->f;
pushdown(u);
pushdown(f);
int d=u==f->ch[1];
int dd=(ff!=null)?(f==ff->ch[1]):0;
if(u->ch[d^1]!=null)u->ch[d^1]->f=f;
f->ch[d]=u->ch[d^1];
u->ch[d^1]=f;
f->f=u;
if(ff!=null)ff->ch[dd]=u;
u->f=ff;
f->rs();
u->rs();
}
void splay(node *u,node *p)
{
pushdown(u);
while(u->f!=p)
{
node *f=u->f;
node *ff=f->f;
if(ff==p)
{
rotate(u);
break;
}
int d=u==f->ch[1];
int dd=f==ff->ch[1];
if(d==dd)rotate(f);
else rotate(u);
rotate(u);
}
if(p==null)root=u;
u->rs();
}
void insert(node *&u,node *&fa,int key)
{
if(u==null)
{
u=newnode(key);
u->f=fa;
splay(u,null);
return ;
}
insert(u->ch[1],u,key);
u->rs();
}
char ss[20];
void find(int k,node *p)//找到排名为k的并旋到p下
{
node *u=root;
while(1)
{
pushdown(u);
if(u->ch[0]->sz+1==k)break;
if(u->ch[0]->sz>=k)u=u->ch[0];
else
{
k-=u->ch[0]->sz+1;
u=u->ch[1];
}
}
splay(u,p);
}
void Add(int l,int r,int x)//本来是要把l-1旋到根,r+1旋到
//根下,加了一个0,就是把l旋到根,r+2旋到根下
{
find(l,null);
find(r+2,root);
node *u=root->ch[1]->ch[0];
u->lazy+=x;
u->key+=x;
u->m+=x;
root->ch[1]->rs();
root->rs();
}
void REVERSE(int l,int r)//本来是操作l-1,r+1选到根
{
find(l,null);
find(r+2,root);
node *u=root->ch[1]->ch[0];
u->rev^=1;
}
void REVOLVE(int l,int r,int x)
//对于[l,r]这段区间向右移t位,相当于把最后t位放到前面
{
int t=(x%(r-l+1)+(r-l+1))%(r-l+1);
//分成两段[l,r-t] [r-t+1,r]
REVERSE(l,r-t);
REVERSE(r-t+1,r);
REVERSE(l,r);
}
void INSERT(int l,int x)
{
find(l+1,null);
find(l+2,root);
root->ch[1]->ch[0]=newnode(x);
root->ch[1]->ch[0]->f=root->ch[1];
root->ch[1]->rs();
root->rs();
splay(root->ch[1]->ch[0],null);
}
void DEL(int x)
{
find(x,null);
find(x+2,root);
root->ch[1]->ch[0]=null;
root->ch[1]->rs();
root->rs();
}
void MIN(int l,int r)
{
find(l,null);
find(r+2,root);
printf("%d\n",root->ch[1]->ch[0]->m);
splay(root->ch[1]->ch[0],null);
}
int main()
{
freopen("memo.in","r",stdin);
freopen("memo.out","w",stdout);
clear();
scanf("%d",&n);
insert(root,null,0);
for(int i=1;i<=n;i++)
{
int x;
scanf("%d",&x);
insert(root,null,x);
}
insert(root,null,1000001);
int l,r,x;
scanf("%d",&m);
for(int i=1;i<=m;i++)
{
scanf("%s",ss);
if(ss[0]=='A')
{
scanf("%d%d%d",&l,&r,&x);
Add(l,r,x);
}
else if(ss[0]=='R'&&ss[3]=='E')
{
scanf("%d%d",&l,&r);
REVERSE(l,r);
}
else if(ss[0]=='R'&&ss[3]=='O')
{
scanf("%d%d%d",&l,&r,&x);
REVOLVE(l,r,x);
}
else if(ss[0]=='I')
{
scanf("%d%d",&l,&x);
INSERT(l,x);
}
else if(ss[0]=='D')
{
scanf("%d",&x);
DEL(x);
}
else
{
scanf("%d%d",&l,&r);
MIN(l,r);
}
}
return 0;
}
继续回顾splay