伸展树
概述:不同于线段树的以空间换取时间,用多余的节点去存储多余的信息,来达到降低时间复杂度。SplayTree基于一种更简单的思想,为了使整个查找时间更小,被查频率高的那些条目就应当经常处于靠近树根的位置。于是想到设计一个简单方法,在每次查找之后对树进行重构,把被查找的条目搬移到离树根近一些的地方。SplayTree应运而生。SplayTree是一种自调整形式的二叉查找树,它会沿着从某个节点到树根之间的路径,通过一系列的旋转把这个节点搬移到树根去。
Splay最基本的操作是旋转,当然前提是在旋转之后,整体仍然是有序的(即中序遍历的结果不变),分为左旋或者右旋,如果要旋转的点x是其父亲结点左儿子,那么执行的是右旋,反之左旋,简单地说就是旋转节点x和它父亲相连的那条边。假如我们定义这棵二叉搜索树父亲节点总是大于它的左儿子,小于它的右儿子。设当前要旋转的节点是x,x节点是父亲是y,y的父亲是z。右旋操作是这样的,将x的右儿子接到y的左儿子上,将y接到x的右儿子上,如果y是z的左儿子,那么x接到z的左儿子,反之,接到右儿子。容易得出这样做的结果使得整棵树仍然是有序的。
将节点x一路旋转到根节点也就重复执行基本的左旋右旋,这也是Splay操作。不过,为了维护树的平衡,Splay操作通常分三种情况:
第一种情况:如果节点z已经是树根,则旋转连接x和y的边。(这种情况是最后一步)
第二种情况:如果z不是树根,而且x和y本身都是左孩子或者都是右孩子,则先旋转连接y和z,然后再旋转连接x和y的边。
第三种情况:如果z不是树根,而且x是左儿子,y是右儿子,或者相反,则先旋转连接x和y的边,再旋转连接x和z的边。
在节点x处进行splay操作的时间是和查找x所需的时间成比例的。这样做的结果是不单是把x搬移到了树根,而且还把查找路径上的每个节点的深度都大致减掉了一半。
在处理区间问题的时候,通常在区间首尾各加入一个多余的节点,这样就不用特判处理的区间端点前面是否还有结点,后面还有没有结点……其实即使不是处理这样的区间问题,多加入一个多余的节点,使树中至少有一个节点,也可以减少很多麻烦和WA……
/*
http://www.lydsy.com/JudgeOnline/problem.php?id=1500
*/
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
#define LL(x) (ch[x][0])
#define RR(x) (ch[x][1])
#define MID(a,b) (a+((b-a)>>1))
#define Kt (ch[ ch[Rt][1] ][0])
#define INF (1<<30)
const int N=5e5+100;
int a[N];
struct SplayTree
{
int Rt,top1,top2;
int pre[N],sz[N],ch[N][2],stk[N],que[N];
int same[N],flag[N],valu[N],flip[N],sum[N],lmx[N],rmx[N],mx[N];
void addNode(int pos,int &x,int f)
{
if(top2) x=stk[--top2];
else x=++top1;
LL(x)=RR(x)=0; pre[x]=f;
flag[x]=flip[x]=0;
valu[x]=a[pos];
sum[x]=lmx[x]=rmx[x]=mx[x]=-INF;
}
inline void iswap(int x)
{
if(x==0)return;
flip[x]^=1;
swap(LL(x),RR(x));
swap(lmx[x],rmx[x]);
}
inline void toSame(int x,int c)
{
if(x==0)return;
flag[x]=1; same[x]=c;
valu[x]=c; sum[x]=sz[x]*c;
lmx[x]=rmx[x]=mx[x]=max(sum[x],valu[x]);
}
void PushDown(int x)
{
if(x==0) return;
if(flip[x])
{
iswap(LL(x)); iswap(RR(x));
flip[x]=0;
}
if(flag[x])
{
toSame(LL(x),same[x]);
toSame(RR(x),same[x]);
flag[x]=0;
}
}
void PushUp(int x)
{
sz[x]=1+sz[LL(x)]+sz[RR(x)];
sum[x]=valu[x]+sum[LL(x)]+sum[RR(x)];
mx[x]=lmx[x]=rmx[x]=-INF;
lmx[x]=max(lmx[LL(x)],sum[LL(x)]+valu[x]+max(0,lmx[RR(x)]));
rmx[x]=max(rmx[RR(x)],sum[RR(x)]+valu[x]+max(0,rmx[LL(x)]));
mx[x]=max(mx[LL(x)],mx[RR(x)]);
mx[x]=max(mx[x],valu[x]+max(0,rmx[LL(x)])+max(0,lmx[RR(x)]));
}
inline void Link(int x,int y,int f)
{
pre[x]=y; ch[y][f]=x;
}
inline void Rotate(int x,int f)//0表示左旋,1表示右旋
{
int y=pre[x],z=pre[y];
PushDown(y); PushDown(x);
Link(ch[x][f],y,!f);
Link(x,z,RR(z)==y);
Link(y,x,f);
PushUp(y);
}
inline void Splay(int x,int goal)//将节点x旋转到goal下面
{
while(pre[x]!=goal)
{
int y=pre[x],z=pre[y];
int cx=(LL(y)==x),cy=(LL(z)==y);
if(z==goal) Rotate(x,cx);
else
{
if(cx==cy) Rotate(y,cy);
else Rotate(x,cx);
Rotate(x,cy);
}
}
PushUp(x);
if(goal==0) Rt=x;
}
inline void Select(int K,int goal)//将第K个数旋转到goal下面。
{
int x=Rt;
PushDown(x);
while(sz[LL(x)]!=K)
{
if(K<sz[LL(x)]) x=ch[x][0];
else K-=sz[LL(x)]+1,x=ch[x][1];
PushDown(x);
}
//printf("find:%d\n",valu[x]);
Splay(x,goal);
}
void Erase(int x)//删除以x为根的子树
{
int father=pre[x];
int head=0,tail=0;
for(que[tail++]=x;head<tail;head++)
{
stk[top2++]=que[head];
if(LL(que[head])) que[tail++]=LL(que[head]);
if(RR(que[head])) que[tail++]=RR(que[head]);
}
ch[father][ RR(father)==x ]=0;
}
void Insert()//插入一段区间
{
int pos,tot;
scanf("%d%d",&pos,&tot);
for(int i=1;i<=tot;i++) scanf("%d",&a[i]);
Select(pos,0), Select(pos+1,Rt);
build(1,tot,Kt,ch[Rt][1]);
Splay(ch[Rt][1],0);
}
void Delete()//删除一段区间
{
int pos,tot;
scanf("%d%d",&pos,&tot);
Select(pos-1,0), Select(pos+tot,Rt);
Erase(Kt);
Splay(ch[Rt][1],0);
}
void MakeSame()//将一段区间内的值全部变成C
{
int pos,tot,c;
scanf("%d%d%d",&pos,&tot,&c);
Select(pos-1,0), Select(pos+tot,Rt);
//debug();
toSame(Kt,c);
Splay(ch[Rt][1],0);
}
void Reverse()//将一段区间进行旋转
{
int pos,tot;
scanf("%d%d",&pos,&tot);
Select(pos-1,0), Select(pos+tot,Rt);
iswap(Kt);
Splay(ch[Rt][1],0);
}
void GetSum()//求一段区间和
{
int pos,tot;
scanf("%d%d",&pos,&tot);
Select(pos-1,0), Select(pos+tot,Rt);
printf("%d\n",sum[Kt]);
}
void MaxSum()//求出子区间和的最大值。
{
Splay(1,0);
Splay(2,1);
printf("%d\n",mx[Kt]);
}
void build(int lft,int rht,int &x,int f)//建树
{
if(lft>rht) return;
int mid=MID(lft,rht);
addNode(mid,x,f);
build(lft,mid-1,LL(x),x);
build(mid+1,rht,RR(x),x);
PushUp(x);
}
void init(int st,int ed)//初始化
{
Rt=top1=top2=0;
sz[0]=pre[0]=LL(0)=RR(0)=0;
lmx[0]=rmx[0]=mx[0]=-INF;
addNode(0,Rt,0); addNode(0,RR(Rt),Rt);
build(st,ed,Kt,RR(Rt));
PushUp(RR(Rt)); PushUp(Rt);
}
void debug() { printf("Rt:%d\n",Rt); travel(Rt); }
void travel(int x)
{
if(x==0) return;
PushDown(x);
travel(LL(x));
printf("node:%d,pre:%d,lson:%d,rson:%d,valu:%d,mx:%d,lmx:%d,rmx:%d\n",
x,pre[x],LL(x),RR(x),valu[x],mx[x],lmx[x],rmx[x]);
travel(RR(x));
}
}spt;
int main()
{
int n,m;
while(scanf("%d%d",&n,&m)!=EOF)
{
a[0]=-INF;
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
spt.init(1,n);
//spt.debug();
char buf[100];
for(int i=0;i<m;i++)
{
scanf("%s",buf);
if(buf[0]=='I') spt.Insert();
else if(buf[0]=='D') spt.Delete();
else if(buf[0]=='M')
{
if(buf[2]=='K')spt.MakeSame();
else spt.MaxSum();
}
else if(buf[0]=='R') spt.Reverse();
else spt.GetSum();
//spt.debug();
}
}
return 0;
}