[题解] BZOJ 3223 文艺平衡树

BZOJ 3223 文艺平衡树

题目描述 Description
您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作:

翻转一个区间,例如原有序序列是5 4 3 2 1,翻转区间是[2,4]的话,结果是5 2 3 4 1
最开始所有元素都是0。

输入描述 Input Description
第一行为n,m
n表示初始序列有n个数,这个序列依次是(1,2……n-1,n)
m表示翻转操作次数
接下来m行每行两个数[l,r] 数据保证 1≤l≤r≤n

输出描述 Output Description
输出一行n个数字,表示原始序列经过m次变换后的结果

样例输入 Sample Input
5 3
1 3
1 3
1 4

样例输出 Sample Output
4 3 2 1 5

数据范围及提示 Data Size & Hint
n,m≤100000

分析:
这个题目要求我们进行旋转操作,怎么办呢?
模拟肯定超时~放弃
题目不是给了你提示么–平衡树
那么我们用Splay来维护这个序列
因为Splay满足二叉搜索树的性质,那么如果把每个点的左右儿子交换,那么这些点的中序遍历就翻转了
于是将l-1和r+1号节点分别提至根节点和根节点的右儿子,那么要操作的序列就是r+1号节点的左儿子,直接给它打上一个翻转标记,等到下次遇到这个点是就下传标记并且翻转左右子树即可(像不像线段树lazy_tag?)

Code:

#include <bits/stdc++.h>
using namespace std;
#define inf 2100000000
#define maxn 100010
int ch[maxn][2],f[maxn],size[maxn],key[maxn],lazy[maxn];
int sz,root;
int read() {
    int ans=0,flag=1;
    char ch=getchar();
    while( (ch>'9' || ch<'0') && ch!='-' ) ch=getchar();
    if(ch=='-') flag=-1,ch=getchar();
    while(ch>='0' && ch<='9') ans=ans*10+ch-'0',ch=getchar();
    return ans*flag;
}
bool get(int x) {return ch[f[x]][1]==x;}
void update(int x) {
    if(x) {
        size[x]=1;
        if(ch[x][0]) size[x]+=size[ch[x][0]];
        if(ch[x][1]) size[x]+=size[ch[x][1]];
    }
    return ;
}
void pushdown(int x) { //push down lazy tag
    if(x && lazy[x]) {
        lazy[ch[x][0]]^=1;
        lazy[ch[x][1]]^=1;
        swap(ch[x][0],ch[x][1]);
        lazy[x]=0;
    }
    return ;
}
void rotate(int x) {
    int old=f[x],oldf=f[old],whichx=get(x);
    pushdown(old);
    pushdown(x);
    ch[old][whichx]=ch[x][whichx^1];
    f[ch[old][whichx]]=old;
    ch[x][whichx^1]=old;
    f[old]=x;
    f[x]=oldf;
    if(oldf)
        ch[oldf][ch[oldf][1]==old]=x;
    update(old);
    update(x);
    return ;
}
void splay(int x,int tar) { //rotate x to tar
    for(int fa;(fa=f[x])!=tar;rotate(x))
        if(f[fa]!=tar)
            rotate(get(x)==get(fa)?fa:x);
    if(!tar)
        root=x;
    return ;
}
int findx(int x) { //by search the ranking return the treenumber
    int now=root;
    while(1) {
        pushdown(now);
        if(ch[now][0]&&x<=size[ch[now][0]])
            now=ch[now][0];
        else {
            int tmp=(ch[now][0]?size[ch[now][0]]:0)+1;
            if(x<=tmp) return now;
            x-=tmp;
            now=ch[now][1];
        } 
    }
}
void insert(int x) {
    if(root==0) {
        sz++;
        ch[sz][0]=ch[sz][1]=f[sz]=0;
        root=sz;
        size[sz]=1;
        key[sz]=x;
        return ;
    }
    int now=root,fa=0;
    while(1) {
        if(x==key[now]) {
            update(now);
            update(fa);
            splay(now,0);
            break;
        }
        fa=now;
        now=ch[now][key[now]<x];
        if(now==0) {
            sz++;
            ch[sz][0]=ch[sz][1]=0;
            f[sz]=fa;
            size[sz]=1;
            ch[fa][key[fa]<x]=sz;
            key[sz]=x;
            update(fa);
            splay(sz,0);
            break;
        }
    }
    return ;
}
void print(int x) {
    pushdown(x);
    if(ch[x][0])
        print(ch[x][0]);
    if(key[x]!=inf && key[x]!=-inf)
        printf("%d ",key[x]);
    if(ch[x][1])
        print(ch[x][1]);
    return ;
}
int main() {
    int n,m,l,r;
    n=read(),m=read();
    insert(-inf);
    for(int i=1;i<=n;i++)
        insert(i);
    insert(inf);
    for(int i=1;i<=m;i++) {
        l=read(),r=read();
        l=findx(l);
        r=findx(r+2);
        splay(l,0);
        splay(r,l);
        lazy[ch[ch[root][1]][0]]^=1;
    }
    print(root);
    return 0;
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值