「UVA11922 」Permutation Transformer【splay】

11922 - Permutation Transformer

题意

  • m个操作,每次将一个区间翻转然后剪切这个区间并粘贴到尾部,求最后的数列

题解

  • splay模板题,注意剪切粘贴的套路操作就行了

代码

#include<cstdio>
#include<iostream>

using namespace std;
#define inf 0x3f3f3f3f
const int maxn=2e5+10;

/*
说明:
此splay树不能同时支持查询rank操作,如果需要同时支持区间翻转和查询rank,则需要写树套树,或者以值为中序写splay树
*/
int n,m,l,r;

struct splay_tree{
    int tot,root,ch[maxn][2],fa[maxn],siz[maxn];//mark第二维表示存不同的标记
    int minn[maxn],val[maxn],mark[maxn][3];
    splay_tree(){      
        tot=2;root=1;
        ch[1][1]=2;fa[2]=1;fa[1]=0;siz[1]=2;siz[2]=1;val[2]=minn[2]=inf;val[1]=minn[1]=-inf;  //插入两个边界的数方便区间翻转和元素删除操作
    }

    int dir(int x){
        return ch[fa[x]][1]==x;
    }

    void pushup(int x){
        siz[x]=1;
        if(ch[x][0]) siz[x]+=siz[ch[x][0]];
        if(ch[x][1]) siz[x]+=siz[ch[x][1]];
    }

    void down(int x){
        if(mark[x][0]){  //翻转标记
            swap(ch[x][0],ch[x][1]);
            mark[ch[x][0]][0]^=1;
            mark[ch[x][1]][0]^=1;
            mark[x][0]=0;
        }
    }

    void rotate(int x){
        int y=fa[x],z=fa[y],k=dir(x);
        down(y);down(x);
        ch[y][k]=ch[x][k^1];fa[ch[x][k^1]]=y;
        ch[z][dir(y)]=x;fa[x]=z;
        ch[x][k^1]=y;fa[y]=x;
        pushup(y);pushup(x);
    }

    void splay(int x,int goal=0){  //将x splay直到成为goal的儿子
        while(fa[x]!=goal){
            int y=fa[x],z=fa[y];
            if(z!=goal) {
                if(dir(x)==dir(y)) rotate(y);
                else rotate(x);
            }
            rotate(x);
        }
        if(!goal) root=x;
    }

    int kth(int x){  //返回位置为x的数的编号,注意若初始化时插入了两个数要加一
        int cur=root;
        while(true){
            down(cur);
            if(ch[cur][0]&&x<=siz[ch[cur][0]]){
                cur=ch[cur][0];
            }else if(x>siz[ch[cur][0]]+1){
                x-=(siz[ch[cur][0]]+1);
                cur=ch[cur][1];
            }else return cur;  
        }
    }

    void insert(int pos,int value){  //x 为插入的位置,value为插入的值
        int cur=kth(pos),rson=kth(pos+1);
        splay(cur),splay(rson,root);
        
        ch[rson][0]=++tot;fa[tot]=rson;
        ch[tot][0]=ch[tot][1]=0;siz[tot]=1;
        val[tot]=minn[tot]=value;
        pushup(rson);pushup(root);
        splay(cur); //splay到根节点,保持树平衡
    }

    void reverse(int l,int r){
        int cur=kth(l),rson=kth(r+2);
        splay(cur);splay(rson,root);
        mark[ch[rson][0]][0]^=1;int tmp=ch[rson][0];
        ch[rson][0]=0;  //剪下这个[l,r]的树枝
        pushup(rson);pushup(root);

        cur=kth(n+1-(r-l+1)),rson=kth(n+2-(r-l+1)); //因为剪去了[l,r],所以把末尾splay的时候要减去(r-l+1)
        splay(cur);splay(rson,root);
        ch[rson][0]=tmp;fa[tmp]=rson;  //把刚才剪下的树枝接上
        pushup(rson);pushup(root);
    }

    void mid_visit(int cur){ //中序遍历
        down(cur);
        if(ch[cur][0]) mid_visit(ch[cur][0]);
        if(val[cur]!=inf&&val[cur]!=-inf)printf("%d\n",val[cur]);
        if(ch[cur][1]) mid_visit(ch[cur][1]);
    }
}tree;



int main()
{
    scanf("%d %d",&n,&m);
    for(int i=1;i<=n;i++) tree.insert(i,i);
    for(int i=1;i<=m;i++){
        scanf("%d %d",&l,&r);
        tree.reverse(l,r);
    }
    tree.mid_visit(tree.root);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值