[Splay区间翻转] Luogu p3391 文艺平衡树

题目链接:https://www.luogu.org/problem/P3391

从题解里学到了直接建树,就不需要一个个insert了。

假设我们要翻转区间[L,R],可以先把L-1对应的结点转到根root,R+1对应的结点转到根的右节点right,此时right的左子树就是在[L,R]范围内的结点啦。此时把这个子树每个结点的左右儿子都翻一下,整个区间就翻过来了呢。

然鹅没有必要全部翻过来,打个标记就好了,后面每次用到的时候都pushdown下去。

需要注意的是,因为翻转[1,n]的时候,木有0和n+1,所以建树前加了个-inf和inf。

#include<bits/stdc++.h>
#define lchild(x) (T[x].ch[0])
#define rchild(x) (T[x].ch[1])
#define fa(x) (T[x].fa)
#define root (T[0].ch[1])
using namespace std;
typedef long long ll;
const int maxn=1e5+5;
int cnt;
ll ans=1e18;
int tag[maxn];
struct node{
    int val,ch[2],num,sz,fa;//value,孩子,该value的个数,子树中所有value的num和,父亲
}T[maxn];
inline int newnode(int u,int fa,int num){//增加一个value为u,父亲为fa的结点,个数记为num个
    T[++cnt].fa=fa;T[cnt].val=u;
    T[cnt].sz=1;T[cnt].num=num;
    lchild(cnt)=rchild(cnt)=0;//***初始化
    return cnt;
}
bool ident(int x){return lchild(fa(x))==x?0:1;}
void link(int x,int fa,int dir){T[fa(x)=fa].ch[dir]=x;}
void update(int x){
     T[x].sz=T[lchild(x)].sz+T[rchild(x)].sz+T[x].num;}

inline void pushdown(int x){
    if(x&&tag[x]){
        tag[lchild(x)]^=1;
        tag[rchild(x)]^=1;
        tag[x]=0;
        swap(lchild(x),rchild(x));
    }
}
void rotate(int x){
    int dir=ident(x),Y=fa(x),Z=fa(Y);
    link(T[x].ch[dir^1],Y,dir);
    link(x,Z,ident(Y));
    link(Y,x,dir^1);
    update(Y);update(x);//此处要先更新Y再更新x,因为此时Y是x的孩子
}
void splay(int x,int to){//x==to也可以处理
    to=fa(to);
    while(fa(x)!=to){
        if(fa(fa(x))==to)rotate(x);
        else if(ident(x)==ident(fa(x))) rotate(fa(x)),rotate(x);
        else rotate(x),rotate(x);
    }
}
int get(int now,int dir){//now是点的编号,dir==0找前驱,dir==1找后继,此处是要找的now已经是根的情况下
    now=T[now].ch[dir];
    pushdown(now);
    if(!now) return -1;
    while(T[now].ch[dir^1]) now=T[now].ch[dir^1],pushdown(now);
    return T[now].val;
}
int find(int x){//找到value为x的编号
    int now=root;
    while(1){
        pushdown(now);
        if(T[now].val==x){ splay(now,root);return now;}
        int dir=x<T[now].val?0:1;
        if(!T[now].ch[dir]) return 0;
        now=T[now].ch[dir];
    }
}
void dele(int x){//删掉value为x的点
    x=find(x);//注释掉这句就是删除编号为x的点啦
    if(!x) return;
    //if(T[x].num>1){T[x].num--;T[x].sum--;return ;}//决定是删掉该权值的一个点还是删掉权值的所有点
    if(lchild(x)){
        int pre=lchild(x);
        pushdown(pre);
        while(rchild(pre)) pre=rchild(pre),pushdown(pre);
        splay(pre,lchild(root));
        link(rchild(root),pre,1);
        link(pre,0,1);
        update(pre);
    }else{
        root=rchild(x);
        T[root].fa=0;
    }
}
int kth(int k){//value第k大的点(value)的编号,不去重的哦,去重的话sz对每种value只记1即可
    int now=root;
    while(1){
        pushdown(now);
        int used=T[now].sz-T[rchild(now)].sz;
        if(T[lchild(now)].sz<k&&k<=used){ splay(now,root);return now;}
        if(k<used) now=lchild(now);
        else now=rchild(now),k-=used;
    }
}
int n,m;
int a[maxn];
int build(int fa,int l,int r){
    if(l>r){return 0;}
    int m=l+r>>1;
    int now=newnode(a[m],fa,1);
    lchild(now)=build(now,l,m-1);
    rchild(now)=build(now,m+1,r);
    update(now);
    return now;
}
void rev(int l,int r){
    l=kth(l),r=kth(r);
    splay(l,root);
    splay(r,rchild(root));
    tag[lchild(rchild(root))]^=1;
}
void dfs(int t){
   // printf(" t=%d\n",t);
    pushdown(t);
    if(lchild(t)) dfs(lchild(t));
    if(T[t].val!=1e9&&T[t].val!=-1e9)printf("%d ",T[t].val);
    if(rchild(t)) dfs(rchild(t));
}
int main(){
    scanf("%d%d",&n,&m);
    a[1]=-1e9;a[n+2]=1e9;
    for(int i=2;i<=n+1;i++) a[i]=i-1;
    root=1;
    build(0,1,n+2);
    //dfs(root);
    while(m--){
        int l,r;
        scanf("%d%d",&l,&r);
        rev(l,r+2);
    }
    dfs(root);
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值