第一道Splay 看了一天才能敲 当然模板还是仿照HH的。
把代码理解通透也对splay的理解加深。
似乎没有线段树那么具体,因为不断的旋转操作会使splay 的形状发生变化。
但是二叉排序树原则没有变。然后在每一次query 和 update 的时候都需要的Rotate的两个操作就会得到想要的区间。
与线段树不相同的是他不用开n*4的空间 因为每一个结点代表自身和子树上的信息,在旋转之前就要pushdown,将结点定位以后还要pushup
在刘汝佳的大白上面说的也很清楚。
下面代码上的注释可以帮助理解splay
#include <cstdio>
#include <iostream>
#define inf 0x3f3f3f3f
#define maxn 222222
#define keyTree (ch[ch[root][1]][0])
//当把l-1放在根节点 r+1放在根节点的右子树
//那么根节点的右子树的左子树就是[l,r] 这个区间的所有值
using namespace std;
typedef long long LL;
int S[maxn],que[maxn],ch[maxn][2],pre[maxn],siz[maxn];
int root,top1,top2;
/*以上变量为Splay固有的*/
int add[maxn],val[maxn],a[maxn];
LL sum[maxn];
//debug来自HH
void Treaval(int x)
{
if(x)
{
Treaval(ch[x][0]);
printf("结点%2d:左儿子 %2d 右儿子 %2d 父结点 %2d size = %2d ,val = %2d , sum = %2lld \n",x,ch[x][0],ch[x][1],pre[x],siz[x],val[x],sum[x]);
Treaval(ch[x][1]);
}
}
void debug()
{
printf("root=%d\n",root);
Treaval(root);
}
//建一个新的节点,放在x,S是一个回收内存的内存池
//PRE是x的父亲节点
//v是把这个节点赋值为v
void New(int &x,int PRE,int v)
{
if(top2)x=S[--top2];
else x=++top1;
ch[x][0]=ch[x][1]=0;
siz[x]=1;
pre[x]=PRE;
/*special*/
add[x]=0;
val[x]=v;
sum[x]=v;
}
void pushup(int x)/*special*/
{
siz[x]=siz[ch[x][0]]+siz[ch[x][1]]+1;
sum[x]=val[x]+sum[ch[x][0]]+sum[ch[x][1]];
}
void pushdown(int x)/*special*/
{
if(add[x])
{
val[x]+=add[x];
add[ch[x][0]]+=add[x];
add[ch[x][1]]+=add[x];
sum[ch[x][0]]+=(LL)siz[ch[x][0]]*add[x];
sum[ch[x][1]]+=(LL)siz[ch[x][1]]*add[x];
add[x]=0;
}
}
//以上两个函数跟线段树大致相同
//建树,把中间节点作为根
void build(int &x,int s,int e,int f)
{
if(s>e)return;
int mid=(s+e)>>1;
New(x,f,a[mid]);
if(s<mid)build(ch[x][0],s,mid-1,x);
if(e>mid)build(ch[x][1],mid+1,e,x);
pushup(x);
}
//旋转,0为左旋 1为右旋
void Rotate(int x,int kind)
{
int y=pre[x];
pushdown(x);
pushdown(y);
ch[y][!kind]=ch[x][kind];
pre[ch[x][kind]]=y;
if(pre[y])ch[pre[y]][ch[pre[y]][1]==y]=x;
pre[x]=pre[y];
ch[x][kind]=y;
pre[y]=x;
pushup(y);
//pushu(x);这里可以不用pushupx 是因为splay的时候会pushx
//然而splay的时候也只动了x所以在后面pushx是一样的
//而且复杂度会有很大影响
}
//把x节点放在goal的子树
//当然如果这个结点上的键值比goal大就是在goal的右子树,反之左子树
void Splay(int x,int goal)
{
pushdown(x);
while(pre[x]!=goal)
{
if(pre[pre[x]]==goal)
Rotate(x,ch[pre[x]][0]==x);
else
{
int y=pre[x];
int kind=ch[pre[y]][0]==y;
if(ch[y][kind]==x){
Rotate(x,!kind);
Rotate(x,kind);
}
else {
Rotate(y,kind);
Rotate(x,kind);
}
}
}
pushup(x);
if(goal==0)root=x;
}
//把左数第k号节点放在goal下面
//调用了splay
void RorateTo(int k,int goal)
{
int r=root;
pushdown(r);
while(siz[ch[r][0]]!=k)
{
if(k<siz[ch[r][0]])
{
r=ch[r][0];
}
else
{
k-=siz[ch[r][0]]+1;
r=ch[r][1];
}
pushdown(r);
}
Splay(r,goal);
}
//删除节点x 并回收内存
void erase(int x)
{
int y=pre[x];
int head=0,tail=0;
for(que[tail++]=x;head<tail;head++)
{
S[top2++]=que[head];
if(ch[que[head]][0])que[tail++]=ch[que[head]][0];
if(ch[que[head]][1])que[tail++]=ch[que[head]][1];
}
ch[y][ch[y][1]==x]=0;
pushup(y);
}
//插入节点 键值为k
void insert(int k)
{
int r=root;
while(ch[r][val[r]<k])
r=ch[r][val[r]<k];
New(ch[r][val[r]<k],r,k);
Splay(ch[r][val[r]<k],0);
}
//查询
LL query(int l,int r)
{
RorateTo(l-1,0);
RorateTo(r+1,root);
return sum[keyTree];
}
//修改值 更新
void update(int l,int r)
{
int v;
scanf("%d",&v);
RorateTo(l-1,0);
RorateTo(r+1,root);
add[keyTree]+=v;
sum[keyTree]+=(LL)siz[keyTree]*v;
}
//初始化,包括建树 和 树上信息的输入
void init(int n)/*special*/
{
root=top1=top2=0;
ch[0][0]=ch[0][1]=siz[0]=pre[0]=0;
add[0]=sum[0]=0;
New(root,0,-1);
New(ch[root][1],root,-1);
siz[root]=2;
for(int i=0;i<n;i++)scanf("%d",&a[i]);
build(keyTree,0,n-1,ch[root][1]);
pushup(ch[root][1]);
pushup(root);
}
//得到前驱
int getpre(int x)
{
int tmp=ch[x][0];
if(tmp==0)return inf;
while(ch[tmp][1])tmp=ch[tmp][1];
return val[tmp];
}
//后继
int getnext(int x)
{
int tmp=ch[x][1];
if(tmp==0)return inf;
while(ch[tmp][0])tmp=ch[tmp][0];
return val[tmp];
}
int main()
{
int n,m;
while(scanf("%d%d",&n,&m)!=EOF)
{
init(n);
char str[5];
while(m--)
{
int l,r;
scanf("%s",str);
scanf("%d%d",&l,&r);
if(str[0]=='Q')
printf("%lld\n",query(l,r));
else update(l,r);
}
}
return 0;
}
/*
10 3
1 2 3 4 5 6 7 8 9 10
Q 1 10
*/
下面是线段树
#include <iostream>
#include <cstdio>
#include <algorithm>
#define MAXN 100005
using namespace std;
long long tree[MAXN<<2];
long long add[MAXN<<2];
void pushdown(int num,int mid)
{
if(add[num])
{
add[num<<1] += add[num];
add[num<<1|1] += add[num];
tree[num<<1] += ((mid-(mid>>1)) * add[num]);
tree[num<<1|1] += ((mid>>1) * add[num]);
add[num] = 0;
}
}
void build(int num,int l,int r)
{
add[num]=0;
if(l==r)
{
scanf("%lld",&tree[num]);
return;
}
int mid=(l+r)>>1;
build(num<<1,l,mid);
build(num<<1|1,mid+1,r);
tree[num] = tree[num<<1] + tree[num<<1|1];
}
void update(int num,int s,int e,int l,int r,int val)
{
if(s>=l && e<=r)
{
add[num] += val;
tree[num] += (long long)val * (e - s + 1);
return;
}
pushdown(num,e-s+1);
int mid = (s+e)>>1;
if(l<=mid)update(num<<1,s,mid,l,r,val);
if(r>mid)update(num<<1|1,mid+1,e,l,r,val);
tree[num] = tree[num<<1] + tree[num<<1|1];
}
long long query(int num,int s,int e,int l,int r)
{
if(s==l && e==r)
{
return tree[num];
}
pushdown(num,e-s+1);
int mid=(s+e)>>1;
if(r<=mid)
return query(num<<1,s,mid,l,r);
else
{
if(l>mid)return query(num<<1|1,mid+1,e,l,r);
else return query(num<<1,s,mid,l,mid) + query(num<<1|1,mid+1,e,mid+1,r);
}
}
int main()
{
int n,op;
char str[10];
scanf("%d%d",&n,&op);
build(1,1,n);
for(int i=1;i<=op;i++)
{
//printf("op = %d %d\n",op,n);
int a,b,c;
scanf("%s",str);
if(str[0]=='Q')
{
scanf("%d%d",&a,&b);
printf("%lld\n",query(1,1,n,a,b));
}
else if(str[0]=='C')
{
scanf("%d%d%d",&a,&b,&c);
update(1,1,n,a,b,c);
}
}
return 0;
}
/*
10 10
1 2 3 4 5 6 7 8 9 10
*/