uva11922 - Permutation Transformer 伸展树

本文介绍了一种通过树状结构实现的高效算法,用于处理逆序子序列问题。该算法能够在给定一系列指令的情况下,对初始排列进行逆序操作,并最终输出经过一系列逆序操作后的排列结果。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Permutation Transformer 

Write a program to transform the permutation 1, 2, 3,..., n according to m instructions. Each instruction (a, b) means to take out the subsequence from the a-th to the b-th element, reverse it, then append it to the end.

Input 

There is only one case for this problem. The first line contains two integers n and m (1$ \le$n, m$ \le$100, 000). Each of the next m lines contains an instruction consisting of two integers a and b (1$ \le$a$ \le$b$ \le$n).

Output 

Print n lines, one for each integer, the final permutation.


Explanation of the sample below

Instruction (2,5): Take out the subsequence {2,3,4,5}, reverse it to {5,4,3,2}, append it to the remaining permutation {1,6,7,8,9,10}

Instruction (4,8): The subsequence from the 4-th to the 8-th element of {1,6,7,8,9,10,5,4,3,2} is {8,9,10,5,4}. Take it out, reverse it, and you'll get the sample output.


Warning: Don't use cin, cout for this problem, use faster i/o methods e.g scanf, printf.

Sample Input 

10 2
2 5
4 8

Sample Output 

1
6
7
3
2
4
5
10
9
8

  最开始排列1-N,有M条指令,每条指令(a,b)表示取出第a-b个元素,翻转后添加到排列尾部。

  用split操作把a之前的和整个串分开,再把剩下的串分成左边b-a+1个,剩下的在右边,这样就有了left,mid,right,把mid标记旋转,合并left和right,再和mid合并。

  旋转就和线段树一样,这里就是swap(ch[0],ch[1]),有标记的话要pushdown。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<vector>
#include<set>
#include<utility>
#include<cstring>
#include<stack>
#include<queue>
#include<map>
#include<deque>
#include<cmath>
#include<map>
#define INF 0x3f3f3f3f
#define eps 1e-3
using namespace std;
typedef long long LL;
const int MAXN=100010;
const int MAXM=1030;
const int MOD=1000000007;

int N,M;

struct Node{
    Node* ch[2];
    int s;
    int v;
    int flip;

    int cmp(int k) const{
        int d=k-ch[0]->s;
        if(d==1) return -1;
        return d<=0?0:1;
    }
    void maintain(){
        s=ch[0]->s+ch[1]->s+1;
    }
    void pushdown(){
        if(flip){
            swap(ch[0],ch[1]);
            ch[0]->flip=!ch[0]->flip;
            ch[1]->flip=!ch[1]->flip;
            flip=0;
        }
    }
};

Node* null=new Node();

void rotate(Node*& o,int d){
    Node* k=o->ch[d^1];
    o->ch[d^1]=k->ch[d];
    k->ch[d]=o;
    o->maintain();
    k->maintain();
    o=k;
}
void splay(Node*& o,int k){
    o->pushdown();
    int d=o->cmp(k);
    if(d==1) k-=o->ch[0]->s+1;
    if(d!=-1){
        Node* p=o->ch[d];
        p->pushdown();
        int d2=p->cmp(k);
        int k2=(d2==0?k:k-p->ch[0]->s-1);
        if(d2!=-1){
            splay(p->ch[d2],k2);
            if(d==d2) rotate(o,d^1);
            else rotate(o->ch[d],d);
        }
        rotate(o,d^1);
    }
}
Node* merge(Node* left,Node* right){
    splay(left,left->s);
    left->ch[1]=right;
    left->maintain();
    return left;
}

//k>=1
void split(Node* o,int k,Node*& left,Node*& right){
    splay(o,k);
    left=o;
    right=o->ch[1];
    o->ch[1]=null;
    left->maintain();
}

struct SplaySequence{
    int n;
    Node seq[MAXN];
    Node* root;

    Node* build(int sz){
        if(!sz) return null;
        Node* L=build(sz/2);
        Node* o=&seq[++n];
        o->v=n;
        o->ch[0]=L;
        o->ch[1]=build(sz-sz/2-1);
        o->flip=0;
        o->maintain();
        return o;
    }
    void init(int sz){
        n=0;
        null->s=0;
        root=build(sz);
    }
};

vector<int> ans;
void print(Node* o){
    if(o!=null){
        o->pushdown();
        print(o->ch[0]);
        ans.push_back(o->v);
        print(o->ch[1]);
    }
}

SplaySequence ss;

int main(){
    freopen("in.txt","r",stdin);
    while(scanf("%d%d",&N,&M)!=EOF){
        ss.init(N+1);
        while(M--){
            int a,b;
            scanf("%d%d",&a,&b);
            Node *left,*mid,*right,*o;
            split(ss.root,a,left,o);
            split(o,b-a+1,mid,right);
            mid->flip^=1;
            ss.root=merge(merge(left,right),mid);
        }
        print(ss.root);
        for(int i=1;i<=N;i++) printf("%d\n",ans[i]-1);
    }
    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值