(新知)数据结构--Splay--模板

写了一发splay的模板,在正常的应用中应该是够用了。除了具备splay特有的功能比如区间的分裂与合并,还具有所有treap的功能,同时也可以维护区间的最大值最小值。对于初次学习splay比较有帮助。
有毒!

#include<bits/stdc++.h>
using namespace std;

const int INF=2147483647;
int n;
struct Node
{
    int key,siz;       //键值和大小
    Node* ch[2];       //左右儿子指针
    int add;           //懒标记
    int sum,minv,maxv; //维护的和、最大值、最小值
    bool flip;         //翻转标记
    Node(int key);
    int cmp(int);
    void maintain();
    void push_down();
}*null,*root,*limt;

//初始化结点信息
Node :: Node(int key=0):key(key) 
{
    sum=key;
    maxv=-INF;
    minv=INF;
    siz=(null?1:0);
    ch[0]=ch[1]=null;
    add=flip=0;
}

//返回当前结点向下查找第k个元素的方向
int Node :: cmp(int k) 
{
    if(k==ch[0]->siz+1) return -1;
    else return k<ch[0]->siz+1?0:1;
}

//上传更新数据
void Node :: maintain() 
{
    siz=1+ch[1]->siz+ch[0]->siz;
    minv=maxv=sum=key;
    if(ch[0]!=limt)
        sum+=ch[0]->sum;
        minv=min(minv,ch[0]->minv),
        maxv=max(maxv,ch[0]->maxv);
    if(ch[1]!=limt)
        sum+=ch[1]->sum;
        minv=min(minv,ch[1]->minv),
        maxv=max(maxv,ch[1]->maxv);
    return;
}

//下传标记,先翻转后加add 与 先加add后翻转的效果相同
void Node :: push_down() 
{
    if(flip)
    {
        flip=false;
        swap(ch[0],ch[1]);
        ch[0]->flip^=1;
        ch[1]->flip^=1;
    }
    if(add)
    {
        if(ch[0]!=limt) ch[0]->add+=add;
        if(ch[1]!=limt) ch[1]->add+=add;
        key+=add;
        add=0;
    }
}

//按顺序输出序列
void Print(Node* cur)
{
    if(cur==null) return;
    cur->push_down();
    Print(cur->ch[0]);
    if(cur->key) cout<<cur->key<<" ";
    Print(cur->ch[1]);
}

//旋转,dir==0为左旋,反之为右旋
void Rotate(Node* &cur,int dir) 
{
    Node* tmp=cur->ch[dir^1];
    cur->ch[dir^1]=tmp->ch[dir];
    tmp->ch[dir]=cur;
    cur->maintain();
    tmp->maintain();
    cur=tmp;
}

//将第k个元素伸展到cur结点上
void Splay(Node* &cur,int k) 
{
    cur->push_down();
    int dir=cur->cmp(k);
    if(dir==1) k-=cur->ch[0]->siz+1;
    if(~dir)
    {
        Node* tmp=cur->ch[dir];
        tmp->push_down();
        int dir2=tmp->cmp(k);
        int k2=dir2==1?k-tmp->ch[0]->siz-1:k;
        if(~dir2)
        {
            Splay(tmp->ch[dir2],k2);
            if(dir==dir2) Rotate(cur,dir^1);
            else Rotate(cur->ch[dir],dir);
        }
        Rotate(cur,dir^1);
    }
    return;
}

//合并两个区间到left中
void Merge(Node* &left,Node* right) 
{
    left->push_down();
    Splay(left,left->siz);
    left->ch[1]=right;
    left->maintain();
    return;
}

//把区间分为大小为k的部分和剩余的部分
void Split(Node* o,int k,Node* &left,Node* &right) 
{
    o->push_down();
    Splay(o,k);
    left=o;
    right=o->ch[1];
    o->ch[1]=null;
    left->maintain();
    return;
}

//向序列尾添加键值为_key的元素
void Push_back(int _key) 
{
    Node* add=new Node(_key);
    Merge(root,add);
}

//在pos位置上(第pos个元素后)添加键值为_key的元素
void Insert(int _key,int pos) 
{
    Node* add=new Node(_key);
    pos++;
    Node *left,*right;
    Split(root,pos,left,right);
    Merge(left,add);
    Merge(left,right);
    root=left;
}

//删除在位置pos的元素
void Delete(int pos) 
{
    pos++;
    Node *left,*right,*mid,*g;
    Split(root,pos,g,right);
    Split(g,pos-1,left,mid);
    Merge(left,right);
    root=left;
}

//把从beg开始的长度为len的区间移动到to位置
void Move_seg(int beg,int len,int to) 
{
    beg++,to++;
    Node *left,*mid,*right,*g,*rot=root;
    Split(root,beg-1,left,g);
    Split(g,len,mid,right);
    Merge(left,right);
    Node *l,*r;
    Split(left,to-len,l,r);
    Merge(l,mid);
    Merge(l,r);
    root=l;
}

//将区间a到b加上val,如果flip_mark为真,则同时翻转
void Change(int a,int b,int val,bool flip_mark) 
{
    a++,b++;
    Node *left,*right,*g,*mid;
    Split(root,a-1,left,g);
    Split(g,b-a+1,mid,right);
    mid->add+=val;
    mid->flip^=flip_mark;
    Merge(left,mid);
    Merge(left,right);
    root=left;
}

//查询维护的值
#define mp make_pair
typedef pair<int,pair<int,int> > ppi;
ppi Query(int a,int b) 
{
    a++,b++;
    Node *left,*right,*g,*mid;
    Split(root,a-1,left,g);
    Split(g,b-a+1,mid,right);
    return mp(mid->sum,mp(mid->maxv,mid->minv));
}

int main()
{
    freopen("input.txt","r",stdin);
    freopen("output.txt","w",stdout);

    null=new Node();
    root=new Node(); //虚拟结点防止崩溃
    limt=new Node();

    scanf("%d",&n);
    for(int i=1;i<=n;i++) Push_back(i);
    Print(root); cout<<endl;

    Insert(76,2);
    cout<<"Insert(76,2);\n";
    Print(root); cout<<endl;

    Delete(7);
    cout<<"Delete(7);\n";
    Print(root); cout<<endl;

    Move_seg(3,3,8);
    cout<<"Move_seg(3,3,8);\n";
    Print(root); cout<<endl;

    Change(1,3,1,0);
    cout<<"Change(1,3,1,0);\n";
    Print(root); cout<<endl;

    Change(1,3,0,1);
    cout<<"Change(1,3,0,1);\n";
    Print(root); cout<<endl;

    ppi ans=Query(2,5);
    cout<<"Query(2,5);\n";
    cout<<"sum:"<<ans.first<<" max:"<<ans.second.first<<" min:"<<ans.second.second<<endl;

    return 0;
}

/***
因为有虚拟结点的存在,占用了第一个位置,而且实际上该结点是不存在的,所以每次有涉及结点位置的信息时都要将对应坐标++来与存储的序列对应
***/
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值