Splay专题

建树

void build(int l,int r,int &id,int f){ 
    if(l>r)return;
    int mid=(l+r)>>1;
    New(id,f,A[mid]); 
    build(l,mid-1,ch[id][0],id);
    build(mid+1,r,ch[id][1],id); 
    up(id);  
}
void init(int n){ 
    ch[0][0]=ch[0][1]=sz[0]=par[0]=val[0]=0;//初始化空地址
    sum[0]=dly[0]=0;
    root=0;num=0;
    New(root,0,0);//先把0插入到树中
    New(ch[root][1],root,0);//在把n+1插入到树中 
    for(int i=1;i<=n;i++)scanf("%d",&A[i]);
    build(1,n,ch[ch[root][1]][0],ch[root][1]);//建成一棵比较平衡的二叉树放进去 
    up(ch[root][1]);
    up(root); 
} 

基本模板

void New(int &id,int f,int v){
    id=++num;
    val[id]=v; 
    ch[id][0]=ch[id][1]=0;
    par[id]=f;
    sum[id]=dly[id]=0;
    sz[id]=1;
}

void rotate(int x){
    //rotate部分不需要down了,找到x时,rotateTo函数已经把x所有的父亲都down了 
    int y=par[x];  
    int d=ch[y][0]==x;//d=1右旋,d=0左旋 
    //3根线发生变化
    ch[y][!d]=ch[x][d];
    if(ch[x][d])par[ch[x][d]]=y;//第一根线

    par[x]=par[y];//第二根 
    if(par[y])ch[par[y]][ch[par[y]][1]==y]=x; 

    ch[x][d]=y;par[y]=x;//第三根 
    up(y);
    //x不需要up,因为x还要继续往上旋 
} 
void Splay(int x,int goal){ 
    //当x的父亲和爷爷不在一个方向时,旋两次x
    //否则,先旋父亲,再旋x 
    while(par[x]!=goal){ 
        int y=par[x];
        if(par[y]==goal)rotate(x);
        else{
            int z=par[y];
            if(ch[z][0]==y^ch[y][0]==x)rotate(x);
            else rotate(y);
            rotate(x); 
        } 
    }  
    up(x);
    if(goal==0)root=x; 
}
void rotateTo(int k,int goal){
    //因为0也占了一个位置,所以第k个数,就变成左边有x个数的位置。 
    int p=root;
    down(p);//因为可能有翻转操作,左右儿子先需要交换,每次都先down一下
    while(sz[ch[p][0]]!=k){  
        if(sz[ch[p][0]]>k){//在左儿子中 
            p=ch[p][0];
        }else{//在右儿子中  
            k-=sz[ch[p][0]]+1; 
            p=ch[p][1];
        }
        down(p); 
    }  
    Splay(p,goal);//p就是第k个数所在的内存地址 
}

功能

<1>查找前驱后继
int lower(int x){
int cur=root,mi=1e9;
    while(cur){ 
        if(val[cur]==x)return 0;
        if(val[cur]<x){
            mi=min(mi,x-val[cur]);
            cur=ch[cur][1];
        }else cur=ch[cur][0];
    }return mi;
}
int upper(int x){
    int cur=root,mi=1e9;
    while(cur){ 
        if(val[cur]==x)return 0;
        if(val[cur]>x){
            mi=min(mi,val[cur]-x);
            cur=ch[cur][0];
        }else cur=ch[cur][1];
    }return mi;
}
<2>
void delt(){
    //更新因为删除该节点所改变的信息
    int pre=get_pre();
    int nxt=get_nxt();
    int rot=root;
    if(pre==-1)root=ch[root][1];
    else if(nxt==-1)root=ch[root][0];
    if(~pre&&~nxt){
        Splay(pre,root);
        par[pre]=nxt;//把前驱接在后继上
        ch[nxt][0]=pre;
        root=ch[root][1];//以原来的右儿子为根
    }par[root]=0;
}
<3> lowupper

类似于二分;如果找low。
我们对于val < x 的节点更新答案然后到它的右节点上(使val变大)
反之则到左节点上

int lower(int x){
    int cur=root,ans=-1e9;
    while(cur){ 
        if(val[cur]<=x){
            ans=max(ans,val[cur]);
            cur=ch[cur][1];
        }else cur=ch[cur][0];
    }return ans;
}
int upper(int x){
    int cur=root,ans=1e9;
    while(cur){ 
        if(val[cur]>=x){
            ans=min(ans,val[cur]);
            cur=ch[cur][0];
        }else cur=ch[cur][1];
    }return ans;
}
<4>区间查询和更新

如图我们只需把l-1旋到根在把r+1旋上去那么r+1的左子树即为我们要的区间
这里写图片描述

void update(int l,int r,int v){
    rotateTo(l-1,0);//以l-1为root
    rotateTo(r+1,root);//以r+1为ch[root][1]
    int need=ch[ch[root][1]][0];//need就为[l,r] 
    dly[need]+=v;//delay延迟更新 
    val[need]+=v;
    sum[need]+=1ll*v*sz[need];
}
ll query(int l,int r){
    rotateTo(l-1,0); 
    rotateTo(r+1,root);
    return sum[ch[ch[root][1]][0]];
} 

<5> 翻转和覆盖
利用 down 来延迟更新

    void down_c(int p,int c){
        cov[p]=1;val[p]=c;
        sum[p]=sz[p]*c;
        mx[p]=lsum[p]=rsum[p]=max(c,sum[p]);
    }
    void down_r(int p){
        rev[p]^=1;
        swap(ch[p][0],ch[p][1]);
        swap(lsum[p],rsum[p]);
    }
    void down(int p){
        int l=ch[p][0],r=ch[p][1];
        if(rev[p]){ 
            if(l)down_r(l);
            if(r)down_r(r); 
            rev[p]=0;
        }
        if(cov[p]){  
            if(l)down_c(l,val[p]);
            if(r)down_c(r,val[p]);
            cov[p]=0;
        }
    }

注意在Splay和RotateTo的过程中都有可能要 down

<6>区间插入
直接套用 build 来建一个新子树

    void insert(){ 
        int pos,m;
        scanf("%d %d",&pos,&m);
        for(int i=1;i<=m;i++)scanf("%d",&A[i]); 
        rotateTo(pos,0);
        rotateTo(pos+1,root);
        build(1,m,key,ch[root][1]);//直接使用build来构建 
        up(ch[root][1]);up(root);
    }

<7>区间删除
为了使内存更优,我们用一个 stk 收集被删除的节点

    void Dfs_del(int k){
        stk[++top]=k;
        if(ch[k][0])Dfs_del(ch[k][0]);
        if(ch[k][1])Dfs_del(ch[k][1]);
    }
    void delt(){
        int pos,m;
        scanf("%d %d",&pos,&m);
        selc(pos,pos+m-1);  
        Dfs_del(key);
        key=0;
        up(ch[root][1]);up(root);  
    } 

全家福

#include<bits/stdc++.h>
using namespace std;
const int M=600005,INF=1e9;
int A[M],n,m;
struct SplayT{
    #define key ch[ch[root][1]][0]
    int val[M],sz[M],par[M],ch[M][2];
    bool rev[M],cov[M];
    int stk[M],top;
    int mx[M],lsum[M],rsum[M],sum[M];
    int num,root;
    void New(int &id,int f,int v){
        if(top)id=stk[top--];
        else id=++num;
        val[id]=v;ch[id][0]=ch[id][1]=0;par[id]=f;
        sum[id]=0;sz[id]=1;
        cov[id]=rev[id]=0;
        mx[id]=lsum[id]=rsum[id]=v;
    }
    void down_c(int p,int c){
        cov[p]=1;val[p]=c;
        sum[p]=sz[p]*c;
        mx[p]=lsum[p]=rsum[p]=max(c,sum[p]);
    }
    void down_r(int p){
        rev[p]^=1;
        swap(ch[p][0],ch[p][1]);
        swap(lsum[p],rsum[p]);
    }
    void up(int p){
        sum[p]=val[p];
        sz[p]=1;
        int l=ch[p][0],r=ch[p][1];
        sum[p]+=sum[l]+sum[r];
        sz[p]+=sz[l]+sz[r]; 
        mx[p]=max(max(mx[l],mx[r]),max(0,rsum[l])+val[p]+max(lsum[r],0));
        lsum[p]=max(lsum[l],sum[l]+val[p]+max(lsum[r],0));
        rsum[p]=max(rsum[r],sum[r]+val[p]+max(rsum[l],0));
    } 
    void down(int p){
        int l=ch[p][0],r=ch[p][1];
        if(rev[p]){ 
            if(l)down_r(l);
            if(r)down_r(r); 
            rev[p]=0;
        }
        if(cov[p]){  
            if(l)down_c(l,val[p]);
            if(r)down_c(r,val[p]);
            cov[p]=0;
        }
    }
    void build(int l,int r,int &id,int f){ 
        if(l>r)return;
        int mid=(l+r)>>1;
        New(id,f,A[mid]); 
        build(l,mid-1,ch[id][0],id);
        build(mid+1,r,ch[id][1],id); 
        up(id);  
    }
    void init(int n){
        ch[0][0]=ch[0][1]=sz[0]=par[0]=0; //初始化空地址
        sum[0]=0;val[0]=0;mx[0]=lsum[0]=rsum[0]=-INF;
        rev[0]=cov[0]=0;
        root=0;num=0;top=0;
        New(root,0,-INF);//0
        New(ch[root][1],root,-INF);//n+1
        for(int i=1;i<=n;i++)scanf("%d",&A[i]);
        build(1,n,ch[ch[root][1]][0],ch[root][1]);
        up(ch[root][1]);
        up(root);
    }
    void Splay(int x,int goal=0){
        while(par[x]!=goal){
            int y=par[x];
            if(par[y]!=goal){
                if(ch[par[y]][0]==y^ch[y][0]==x)rotate(x);
                else rotate(y);
            }rotate(x);
        }up(x);
        if(!goal)root=x;
    }
    void rotateTo(int k,int goal){  
        int cur=root;
        down(cur);
        while(sz[ch[cur][0]]!=k){
            if(sz[ch[cur][0]]>k)cur=ch[cur][0];
            else k-=1+sz[ch[cur][0]],cur=ch[cur][1];
            down(cur);
        }Splay(cur,goal);
    }
    void rotate(int x){
        int y=par[x];
        int d=ch[y][0]==x;
        ch[y][!d]=ch[x][d];
        if(ch[x][d])par[ch[x][d]]=y;
        par[x]=par[y];
        if(par[y])ch[par[y]][ch[par[y]][1]==y]=x;
        ch[x][d]=y;
        par[y]=x;
        up(y);
    }
    void selc(int l,int r){
        rotateTo(l-1,0);
        rotateTo(r+1,root);
    }
    void reverse(){
        int l,c;
        scanf("%d %d",&l,&c);
        selc(l,c+l-1);
        down_r(key);
        up(ch[root][1]);up(root);//操作完了搜集信息 
    }   
    void mark_same(){
        int l,c,x;
        scanf("%d %d %d",&l,&c,&x);
        selc(l,c+l-1);
        down_c(key,x);
        up(ch[root][1]);up(root);
    }
    void get_Sum(){
        int l,c;
        scanf("%d %d",&l,&c);
        selc(l,c+l-1);
        printf("%d\n",sum[key]);
    }
    void Dfs_del(int k){
        stk[++top]=k;
        if(ch[k][0])Dfs_del(ch[k][0]);
        if(ch[k][1])Dfs_del(ch[k][1]);
    }
    void delt(){
        int pos,m;
        scanf("%d %d",&pos,&m);
        selc(pos,pos+m-1);  
        Dfs_del(key);
        key=0;
        up(ch[root][1]);up(root);  
    } 
    void insert(){ 
        int pos,m;
        scanf("%d %d",&pos,&m);
        for(int i=1;i<=m;i++)scanf("%d",&A[i]); 
        rotateTo(pos,0);
        rotateTo(pos+1,root);
        build(1,m,key,ch[root][1]);//直接使用build来构建 
        up(ch[root][1]);up(root);
    }
    void get_max(){
        printf("%d\n",mx[root]);
    }
}T;
void solve(){
    scanf("%d %d",&n,&m);
    char s[215];
    T.init(n);
    for(int i=1;i<=m;i++){
        scanf("%s",s);
        if(s[0]=='I')T.insert();
        if(s[0]=='M'){
            if(s[2]=='X')T.get_max();
            else T.mark_same();
        }if(s[0]=='D')T.delt();
        if(s[0]=='R')T.reverse();
        if(s[0]=='G')T.get_Sum();
    }
}
int main(){
    solve();
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值