LibreOJ105 - 文艺平衡树 (平衡树)

Description

这是一道模板题。

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

翻转一个区间,例如原有序序列是 5 4 3 2 1,翻转区间是 [2,4] 的话,结果是 5 2 3 4 1。

思路

这题体现了无旋treap的区间操作。

注意不能按照关键值来排序,要按照当前结点的位置来排序。更新过程中维护好当前结点之前有多少结点(cnt)。

这样treap就可以把区间分裂出来。分裂后再合并,会改变treap的结构,却不改变treap的中序遍历。
所以如果有一个有序序列,将它们插入到treap中,分裂的treap的中序遍历就是这个有序序列某个子序列。所以可以把这个treap看成这个子序列,对其进行各种操作。

翻转区间就是把区间[l,r]的treap分裂出来,然后交换每个结点的左右子结点。

为什么可以这样做呢?因为中序遍历的顺序是:左结点,此结点,右结点。这样就可以按顺序(比如从小到大,看你结点关系怎么组织)输出。
如果你swap子树的每个结点的左右结点,相当于以右结点,此结点,左结点的顺序来遍历,结果就是顺序反过来(逆序),相当于翻转。可以使用懒惰标记(类似线段树),来减少不必要的翻转。

而且交换每个结点的左右子结点不会改变堆的性质,使得翻转后还是个合法的treap。

#include <iostream>
#include <cstdio>
#include <queue>
#include <algorithm>
#include <map>
#include <set>
#include <vector>
#include <cstring>
#include <string>
#include <stack>
#include <deque>
#include <cmath>
#include <iomanip>
#include <cctype>
 
#define endl '\n'
#define IOS std::ios::sync_with_stdio(0);
#define FILE freopen("..//data_generator//in.txt","r",stdin),freopen("res.txt","w",stdout)
#define FI freopen("..//data_generator//in.txt","r",stdin)
#define FO freopen("res.txt","w",stdout)
#define pb cpush_back
#define mp make_pair
#define seteps(N) fixed << setprecision(N) 
 
typedef long long ll;
using namespace std;
/*-----------------------------------------------------------------*/
 
#define INF 0x3f3f3f3f
const int N = 1e6 + 10;
const int M = 1e9 + 7;
const double eps = 1e-8;

int pos[N];
int lc[N], rc[N];
int val[N];
int cnt[N];
int lazy[N];
int si;

typedef pair<int, int> PII;

void pushdown(int rt) {
    if(lazy[rt]) {
        swap(lc[rt], rc[rt]);
        lazy[lc[rt]]++;
        lazy[rc[rt]]++;
        lazy[lc[rt]] %= 2;
        lazy[rc[rt]] %= 2;
        lazy[rt] = 0;
    }
}

PII split(int rt, int key) {
    if(!key || !rt) {
        return mp(0, rt);
    }
    pushdown(rt);
    if(key < cnt[lc[rt]] + 1) {
        PII o = split(lc[rt], key);
        lc[rt] = o.second;
        cnt[rt] = cnt[lc[rt]] + cnt[rc[rt]] + 1;
        return mp(o.first, rt);
    } else {
        PII o = split(rc[rt], key - cnt[lc[rt]] - 1);
        rc[rt] = o.first;
        cnt[rt] = cnt[lc[rt]] + cnt[rc[rt]] + 1;
        return mp(rt, o.second);
    }
    
}

int merge(int lrt, int rrt) {
    if(!lrt) return rrt;
    if(!rrt) return lrt;
    pushdown(lrt);
    pushdown(rrt);
    if(pos[lrt] > pos[rrt]) {
        rc[lrt] = merge(rc[lrt], rrt);
        cnt[lrt] = cnt[lc[lrt]] + cnt[rc[lrt]] + 1;
        return lrt;
    } else {
        lc[rrt] = merge(lrt, lc[rrt]);
        cnt[rrt] = cnt[lc[rrt]] + cnt[rc[rrt]] + 1;
        return rrt;
    }
}



int insert(int v, int rt) {
    ++si;
    val[si] = v;
    pos[si] = rand();
    cnt[si] = 1;
    return merge(rt, si);
}


void print(int rt) {
    if(!rt) return ;
    pushdown(rt);
    print(lc[rt]);
    cout << val[rt] << " "; 
    print(rc[rt]);
}


int reverse(int l, int r, int rt) {
    int tar;
    PII o1 = split(rt, r);
    tar = o1.first;
    PII o2 = o1;
    if(l > 1) {
        o2 = split(tar, l - 1);
        tar = o2.second;
    }
    lazy[tar] = 1;
    if(l > 1) return merge(merge(o2.first, o2.second), o1.second);
    return merge(o1.first, o1.second);
}

int main() {
    IOS;
    //FO;
    int n, m;
    cin >> n >> m;
    int rt = 0;
    for(int i = 1; i <= n; i++) {
        rt = insert(i, rt);
    }
    while(m--) {
        int l, r;
        cin >> l >> r;
        rt = reverse(l, r, rt);
    }
    print(rt);
}
posted @ 2020-06-27 13:23  limil  阅读(110)  评论(0编辑  收藏  举报