伸展树模板小结(Splay Tree)

来源:互联网 发布:淘宝好货源深圳 编辑:程序博客网 时间:2024/04/28 13:21

首先推荐网上看到的四篇好文章,便于大家对于伸展树有一个直观的的了解:


伸展树的旋转和伸展操作:点击打开链接

伸展树的初步应用—解决区间问题:点击打开链接

伸展树解决区间问题:点击打开链接

伸展树结点的SIZE域的应用:点击打开链接


下面是 kuangbin 的 BZOJ 1500 维护数列的模板~

个人加上了一点自己的理解,对于模板多余的地方也有些修改~~~

//Splay_tree#include <iostream>#include <algorithm>#include <cstdio>#include <cstring>#define Key_value ch[ch[root][1]][0]//在对区间操作的时候,Key_value就是要返回的值//首先把第pos个数旋转(因为包含根节点root~)至根节点,然后把//第(pos+区间长度+1)的数旋转为根节点的右儿子,所以此时Key_value对应的子树就是要求的区间using namespace std;const int MAXN=500010;const int INF=0x3f3f3f3f;//这里是伸展树对应的结构~int pre[MAXN],ch[MAXN][2],key[MAXN],size[MAXN];int root,tot1;//求和标记,翻转标记,修改的延迟标记int sum[MAXN],rev[MAXN],same[MAXN];//求最大字段和所需要的标记int lx[MAXN],rx[MAXN],mx[MAXN];//这里是移除子树后的容量池int s[MAXN],tot2;//原数组int a[MAXN];int n,q;//旋转过程中要维护的伸展树本身属性的域:size[]//                        求和性质的域:sum[]//                        区间翻转性质的域:rev[]//                        修改相同标记的域:same[]//                        最大子段和的域:lx[],rx[],mx[]void NewNode(int &r,int father,int k){    //如果删去子树有多余空出的容量,那么先使用此容量    if(tot2) r=s[tot2--];    else r=++tot1;    pre[r]=father;    ch[r][0]=ch[r][1]=0;    key[r]=k;    sum[r]=k;    rev[r]=same[r]=0;    lx[r]=rx[r]=mx[r]=k;    size[r]=1;}//这里rev[],same[]标记为真表示父节点的对应信息还没有更新到子节点void Update_Rev(int r){    if(!r) return;    swap(ch[r][0],ch[r][1]);    swap(lx[r],rx[r]);    //对于翻转操作不影响size[],sum[],mx[]域的信息}void Update_Same(int r,int v){    if(!r) return;    key[r]=v;    sum[r]=v*size[r];    lx[r]=rx[r]=mx[r]=max(v,v*size[r]);    same[r]=1;    //对于成段更新操作不影响size[]域~}//把子节点更新的信息更新至父节点inline void push_up(int r){    int lson=ch[r][0],rson=ch[r][1];    size[r]=size[lson]+size[rson]+1;    sum[r]=sum[lson]+sum[rson]+key[r];    lx[r]=max(lx[lson],sum[lson]+key[r]+max(0,lx[rson]));    rx[r]=max(rx[rson],sum[rson]+key[r]+max(0,rx[lson]));    mx[r]=max(0,rx[lson])+key[r]+max(0,lx[rson]);    mx[r]=max(mx[r],max(mx[lson],mx[rson]));}//把父节点的标记信息更新到子节点inline void push_down(int r){    if(same[r])    {        Update_Same(ch[r][0],key[r]);        Update_Same(ch[r][1],key[r]);        same[r]=0;    }    if(rev[r])    {        Update_Rev(ch[r][0]);        Update_Rev(ch[r][1]);        rev[r]=0;    }}//以father为根建立一棵子树void Build(int &x,int l,int r,int father){    if(l>r) return;    int mid=(l+r)/2;    NewNode(x,father,a[mid]);    Build(ch[x][0],l,mid-1,x);    Build(ch[x][1],mid+1,r,x);    push_up(x);}void Init(){    root=tot1=tot2=0;    ch[root][0]=ch[root][1]=size[root]=pre[root]=0;    same[root]=rev[root]=sum[root]=key[root]=0;    lx[root]=rx[root]=mx[root]=-INF;    //建立第一个结点    NewNode(root,0,-1);    //建立最后一个结点    NewNode(ch[root][1],root,-1);    for(int i=0;i<n;i++)        scanf("%d",&a[i]);    Build(Key_value,0,n-1,ch[root][1]);    push_up(ch[root][1]);    push_up(root);}/***************************************************////核心代码,维护伸展树旋转过程中的结构不被破坏//将x结点设置为p结点的kind儿子inline void Link(int p,int kind,int x){    ch[p][kind]=x;    pre[x]=p;}//旋转方式,0为左旋,1为右旋//右儿子左旋,左儿子右旋inline void Rotate(int x,int kind){    int p=pre[x];    //每次对一个结点旋转的时候,首先要把其父节点的信息更新    //然后把此节点的信息更新至子节点    //维护了延迟标记的完整性    push_down(p);    push_down(x);    //重新确定3对父子关系    //父亲-种类-儿子    //这里儿子变了的有:pre[p],p,x    //这里父亲变了的有:ch[x][kind],x,p    Link(p,!kind,ch[x][kind]);    Link(pre[p],ch[pre[p]][1]==p,x);    Link(x,kind,p);    //原父节点的儿子变了,所以需要更新信息    push_up(p);}//Splay调整,将r结点调整到goal下面void Splay(int r,int goal){    push_down(r);    while(pre[r]!=goal)    {        int p=pre[r];        if(pre[p]==goal) Rotate(r,ch[pre[r]][0]==r);        else        {            //kind表示该节点是左儿子还是右儿子            //左儿子返回1,右儿子返回0            int kind=ch[pre[p]][0]==p;            ch[p][kind]==r?Rotate(r,!kind):Rotate(p,kind);            Rotate(r,kind);        }    }    //最后更新旋转结点    push_up(r);    if (goal==0) root=r;}/***************************************************///对于伸展树来说,中序遍历的结果就是原数组//所以利用size[]域可求得区间解决k_th问题int Get_kth(int r,int k){    push_down(r);    int t=size[ch[r][0]]+1;    if(t==k) return r;    if(t>k) return Get_kth(ch[r][0],k);    else return Get_kth(ch[r][1],k-t);}void Insert(int pos,int tot){    for(int i=0;i<tot;i++) scanf("%d",&a[i]);    Splay(Get_kth(root,pos+1),0);    Splay(Get_kth(root,pos+2),root);    Build(Key_value,0,tot-1,ch[root][1]);    push_up(ch[root][1]);    push_up(root);}//删除子树void erase(int r){    if(!r) return;    s[++tot2]=r;    erase(ch[r][0]);    erase(ch[r][1]);}void Delete(int pos,int tot){    Splay(Get_kth(root,pos),0);    Splay(Get_kth(root,pos+tot+1),root);    erase(Key_value);    Key_value=0;    push_up(ch[root][1]);    push_up(root);}//将第pos个数开始的连续tot个数修改为cvoid Make_Same(int pos,int tot,int c){    Splay(Get_kth(root,pos),0);    Splay(Get_kth(root,pos+tot+1),root);    Update_Same(Key_value,c);    push_up(ch[root][1]);    push_up(root);}//将第pos个数开始的连续tot个数进行反转void Reverse(int pos,int tot){    Splay(Get_kth(root,pos),0);    Splay(Get_kth(root,pos+tot+1),root);    Update_Rev(Key_value);    push_up(ch[root][1]);    push_up(root);}//得到第pos个数开始的连续tot个数的和int Get_Sum(int pos,int tot){    Splay(Get_kth(root,pos),0);    Splay(Get_kth(root,pos+tot+1),root);    return sum[Key_value];}//得到第pos个数开始的tot个数中最大的子段和int Get_MaxSum(int pos,int tot){    Splay(Get_kth(root,pos),0);    Splay(Get_kth(root,pos+tot+1),root);    return mx[Key_value];}

下面是我对于模板增加的地方~

成段修改增加或者减少~

测试地点:学校oj(Orz,Orz) 点击打开链接

///Splay_tree#include <iostream>#include <algorithm>#include <cstdio>#include <cstring>#define Key_value ch[ch[root][1]][0]using namespace std;typedef long long LL;const int MAXN=100010;int pre[MAXN],ch[MAXN][2],key[MAXN],size[MAXN];int root,tot1;int mod[MAXN];LL sum[MAXN];int a[MAXN];int n,q;void NewNode(int &r,int father,int k){    r=++tot1;    pre[r]=father;    ch[r][0]=ch[r][1]=0;    key[r]=k;    sum[r]=k;    mod[r]=0;    size[r]=1;}void Update_modify(int r,int v){    if(!r) return;    key[r]+=v;    sum[r]+=v*size[r];    mod[r]+=v;}//把子节点更新的信息更新至父节点inline void push_up(int r){    int lson=ch[r][0],rson=ch[r][1];    size[r]=size[lson]+size[rson]+1;    sum[r]=sum[lson]+sum[rson]+key[r];}//把父节点的标记信息更新到子节点inline void push_down(int r){    if(mod[r])    {        Update_modify(ch[r][0],mod[r]);        Update_modify(ch[r][1],mod[r]);        mod[r]=0;    }}//以father为根建立一棵子树void Build(int &x,int l,int r,int father){    if(l>r) return;    int mid=(l+r)/2;    NewNode(x,father,a[mid]);    Build(ch[x][0],l,mid-1,x);    Build(ch[x][1],mid+1,r,x);    push_up(x);}void Init(){    root=tot1=0;    ch[root][0]=ch[root][1]=size[root]=pre[root]=0;    mod[root]=sum[root]=key[root]=0;    //建立第一个结点    NewNode(root,0,-1);    //建立最后一个结点    NewNode(ch[root][1],root,-1);    for(int i=0;i<n;i++) scanf("%d",&a[i]);    Build(Key_value,0,n-1,ch[root][1]);    push_up(ch[root][1]);    push_up(root);}inline void Link(int p,int kind,int x){    ch[p][kind]=x;    pre[x]=p;}inline void Rotate(int x,int kind){    int p=pre[x];    push_down(p);    push_down(x);    Link(p,!kind,ch[x][kind]);    Link(pre[p],ch[pre[p]][1]==p,x);    Link(x,kind,p);    push_up(p);}void Splay(int r,int goal){    push_down(r);    while(pre[r]!=goal)    {        int p=pre[r];        if(pre[p]==goal)        {            Rotate(r,ch[pre[r]][0]==r);        }        else        {            int kind=ch[pre[p]][0]==p;            ch[p][kind]==r?Rotate(r,!kind):Rotate(p,kind);            Rotate(r,kind);        }    }    push_up(r);    if (goal==0) root=r;}int Get_kth(int r,int k){    push_down(r);    int t=size[ch[r][0]]+1;    if(t==k) return r;    if(t>k) return Get_kth(ch[r][0],k);    else return Get_kth(ch[r][1],k-t);}//成段修改增加值void modify(int pos,int tot,int c){    Splay(Get_kth(root,pos),0);    Splay(Get_kth(root,pos+tot+1),root);    Update_modify(Key_value,c);    push_up(ch[root][1]);    push_up(root);}//查询区间和LL Get_Sum(int pos,int tot){    Splay(Get_kth(root,pos),0);    Splay(Get_kth(root,pos+tot+1),root);    return sum[Key_value];}int main(){    int l,r,c,nofcase=1;    char op;    while(scanf("%d%d",&n,&q)!=EOF)    {        Init();        printf("Case %d:\n",nofcase++);        while(q--)        {            cin>>op;            scanf("%d%d",&l,&r);            if(op=='C')            {               scanf("%d",&c);               modify(l,r-l+1,c);            }            else printf("%I64d\n",Get_Sum(l,r-l+1));        }    }    return 0;}