poj 3580 SuperMemo (splay模板)
题意
给出一个数字序列,有6种操作:
(1)ADD x y d: 第x个数到第y个数加d 。
(2)REVERSE x y : 将区间[x,y]中的数翻转 。
(3)REVOLVE x y t :将区间[x,y]旋转t次。
(4)INSERT x p :在第x个数后面插入p 。
(5)DELETE x :删除第x个数 。
(6)MIN x y : 查询区间[x,y]中的最小值 。
思路
splay板子题,对于区间[l,r],将l-1旋为树根,将r+1旋为根的右孩子,那么r+1的左子树就是区间[l,r]。
区间旋转就是把区间分成两段,然后交换一下,因此我们可以把后一段区间处理到一个子树上,保存然后删除,接着再处理到前一段区间的前面。
#include<cstdio>
#include<iostream>
using namespace std;
const int maxx = 2e5+10;
const int inf = 0x3f3f3f3f;
int ch[maxx][2],fa[maxx],siz[maxx],key[maxx];
int rev[maxx],lazy[maxx],w[maxx],mi[maxx];
int rt,sz;
char s[20];
int get(int x)
{
return ch[fa[x]][1]==x;
}
void update(int x)
{
if(!x)return;
siz[x]=siz[ch[x][0]]+siz[ch[x][1]]+1;
mi[x]=key[x];
if(ch[x][0])mi[x]=min(mi[x],mi[ch[x][0]]);
if(ch[x][1])mi[x]=min(mi[x],mi[ch[x][1]]);
}
void pushdown(int x)
{
if(!x)return;
if(rev[x])
{
rev[ch[x][0]]^=1;
rev[ch[x][1]]^=1;
swap(ch[x][0],ch[x][1]);
rev[x]=0;
}
if(lazy[x])
{
lazy[ch[x][0]]+=lazy[x];
lazy[ch[x][1]]+=lazy[x];
key[ch[x][0]]+=lazy[x];
key[ch[x][1]]+=lazy[x];
mi[ch[x][0]]+=lazy[x];
mi[ch[x][1]]+=lazy[x];
lazy[x]=0;
}
}
void rotate(int x)
{
int y=fa[x],z=fa[y],k=get(x);
pushdown(x);pushdown(y);
ch[y][k]=ch[x][k^1];fa[ch[y][k]]=y;
ch[x][k^1]=y;fa[y]=x;fa[x]=z;
if(z)ch[z][ch[z][1]==y]=x;
update(y);update(x);
}
void splay(int x,int goal)
{
for(int y;(y=fa[x])!=goal;rotate(x))
if(fa[y]!=goal)rotate((get(x)==get(y))?y:x);
if(goal==0)rt=x; //注意
}
int findkth(int k) //找第k个节点
{
int x=rt;
while(1)
{
pushdown(x);
if(k<=siz[ch[x][0]])x=ch[x][0];
else
{
k-=siz[ch[x][0]]+1;
if(!k)return x;
x=ch[x][1];
}
}
}
int build(int l,int r,int f)
{
if(l>r)return 0;
int mid=(l+r)/2;
int x=++sz;
fa[x]=f;
key[x]=w[mid];
ch[x][0]=build(l,mid-1,x);
ch[x][1]=build(mid+1,r,x);
update(x);
return x;
}
void add(int l,int r,int val) //区间加
{
int x=findkth(l-1),y=findkth(r+1);
splay(x,0);splay(y,x);
int tmp=ch[ch[rt][1]][0];
key[tmp]+=val;
mi[tmp]+=val;
lazy[tmp]+=val;
update(ch[rt][1]);
update(rt);
}
void Insert(int k,int val) //在第k个数后插入值为x的节点
{
int x=findkth(k),y=findkth(k+1);
splay(x,0);splay(y,x);
ch[ch[rt][1]][0]=++sz;
fa[sz]=ch[rt][1];
key[sz]=mi[sz]=val;
siz[sz]=1;
update(ch[rt][1]);
update(rt);
}
void Delete(int k) //删除第k个节点
{
int x=findkth(k-1),y=findkth(k+1);
splay(x,0);splay(y,x);
ch[ch[rt][1]][0]=0;
update(ch[rt][1]);
update(rt);
}
void Reverse(int l,int r) //区间翻转
{
int x=findkth(l-1),y=findkth(r+1);
splay(x,0); //x旋转为根
splay(y,x); //y旋转为根的右孩子
//旋转完之后y的左子树为区间[l,r]
rev[ch[ch[rt][1]][0]]^=1; //lazy标记是否要旋转其左右子树
}
void revolve(int l1,int r1,int l2,int r2) //区间旋转
{
int x=findkth(l2-1),y=findkth(r2+1);
splay(x,0);splay(y,x);
int tmp=ch[ch[rt][1]][0];ch[ch[rt][1]][0]=0;
update(ch[rt][1]);update(rt);
x=findkth(l1-1);y=findkth(l1);
splay(x,0);splay(y,x);
ch[ch[rt][1]][0]=tmp;
fa[tmp]=ch[rt][1];
update(ch[rt][1]);update(rt);
}
int getmi(int l,int r) //找区间最小值
{
int x=findkth(l-1),y=findkth(r+1);
splay(x,0);splay(y,x);
return mi[ch[ch[rt][1]][0]];
}
int main()
{
int n,m;
scanf("%d",&n);
w[1]=-inf;w[n+2]=inf;
for(int i=2;i<=n+1;i++)scanf("%d",&w[i]);
rt=build(1,n+2,0);
scanf("%d",&m);
int x,y,z;
while(m--)
{
scanf("%s",s);
if(s[0]=='A')scanf("%d%d%d",&x,&y,&z),add(x+1,y+1,z);
else if(s[0]=='I')scanf("%d%d",&x,&y),Insert(x+1,y);
else if(s[0]=='D')scanf("%d",&x),Delete(x+1);
else if(s[0]=='M')scanf("%d%d",&x,&y),printf("%d\n",getmi(x+1,y+1));
else if(s[3]=='E')scanf("%d%d",&x,&y),Reverse(x+1,y+1);
else
{
scanf("%d%d%d",&x,&y,&z);
z=z%(y-x+1);
if(z)revolve(x+1,y-z+1,y-z+2,y+1);
}
}
return 0;
}