题解 P2824排序

简要题意
考虑加强版,给定一个排列,有如下操作:

  • 将 $[l, r]$ 升序排序
  • 将 $[l, r]$ 降序排序
  • 求第 $q$ 位置上的数

对于弱化版只有一次询问,我们可以采取二分答案,复杂度为 $O(n\log^2n)$
强化版二分答案的复杂度甚至劣于暴力,十分不可取。于是我们想能否采取其他方法。
对于一个序列,我们可以把每个数放进权值线段树中,这相当于完成了一次排序。求第 $k$ 个位置上的数可以在线段树上二分,单次操作复杂度为 $O(\log n)$。当我们要对某个区间排序,而他的子区间已经排完序的时候,可以通过线段树合并做到 $O(\log n)$ 的复杂度进行排序。因此我们只需要了解每一颗线段树是维护那个区间的就可以进行合并,这点我们可以使用 $ODT$ 来维护。然而,目标区间的端点可能在某个已拍完序的区间中间,这时候我们要将这段区间分开,使用线段树分裂,复杂度仍然是 $O(\log n)$ 的。总复杂度 $O(n\log n\log \log n)$ 跑的飞快,用时仅需 $893$ $ms$, 优于二分答案的 $5.10$ $s$。

#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 50;
int n, m, q;
struct Segment_Tree{ 
    int rt[N * 40], son[N * 40][2], val[N * 40], poor[N * 40];
    int delcnt, rtc, cnt;
    #define ls(x) son[x][0]
    #define rs(x) son[x][1]
    int new_node() {return delcnt? poor[delcnt--] : ++cnt;} 
    void del(int r) {
        val[r] = son[r][0] = son[r][1] = 0;
        poor[++delcnt] = r;
    }
    void split(int x, int  &y, int k) {
        if(!x) return ;
        y = new_node();
        int v = val[ls(x)];
        if(k > v) split(rs(x), rs(y), k - v);
        else swap(rs(x), rs(y));
        if(k < v) split(ls(x), ls(y), k);
        val[y] = val[x] - k;
        val[x] = k;
    }
    int merge(int x, int y) {
        if(!x || !y) return x + y;
        val[x] += val[y];
        ls(x) = merge(ls(x), ls(y));
        rs(x) = merge(rs(x), rs(y));
        del(y);
        return x;
    }
    void update(int &p, int l, int r, int x, int y) {
        if(!p) p = new_node();
        val[p] += y;
        if(l == r) return;
        int mid = (l + r) >> 1;
        if(x <= mid) update(ls(p), l, mid, x, y);
        else update(rs(p), mid + 1, r, x, y);
    }
    int kth(int p, int l, int r, int k) {
        if(!p) return -1;
        if(l == r) return l;
        int mid = (l + r) >> 1, v = val[ls(p)];
        if(k <= v) return kth(ls(p), l, mid, k);
        else return kth(rs(p), mid + 1, r, k - v);
    }
}tc;
struct ODT{
    int l, r, tag, rc;
    ODT (int l = 0, int r = 0, int rc = 0, int tag = 0) : l(l), r(r), rc(rc), tag(tag) {} 
    bool operator<(const ODT o) const {return l < o.l;} 
};
set <ODT> rg;
auto split(int pos) {
    auto it = rg.lower_bound(ODT(pos));
    if(it != rg.end() && it->l == pos) return it;
    it--;
    int l = it->l, r = it->r, tag = it->tag;
    int r1 = it->rc, r2 = 0;
    if(it->tag == 1) {
        tc.split(r1, r2, (r - pos + 1));
        swap(r1, r2);
    }   
    else tc.split(r1, r2, (pos - l)); // !!!
    rg.erase(it);
    rg.insert(ODT(l, pos - 1, r1, tag));
    return rg.insert(ODT(pos, r, r2, tag)).first;
}
auto Sort(int l, int r, int opt) {
    auto end = split(r + 1), begin = split(l);
    ODT ck(l, r, 0, opt);
    for(auto it = begin ; it != end ; it++) {
        ck.rc = tc.merge(ck.rc, it->rc);
    }
    rg.erase(begin, end);
    rg.insert(ck);
}
int Rank(int pos) {
    auto it = rg.lower_bound(ODT(pos));
    if(it->l != pos) it--;
    int rk = pos - it->l + 1;
    if(it->tag) rk = (it->r - it->l + 1) - rk + 1;
    return tc.kth(it->rc, 1, n, rk);
}
int main() {
    int opt, l, r, x;
    scanf("%d %d", &n, &m);
    for(int i = 1;i <= n;i++) {
        scanf("%d", &x);
        tc.update(tc.rt[++tc.rtc], 1, n, x, 1);
        rg.insert(ODT(i, i, tc.rt[tc.rtc]));
    }
    for(int i = 1;i <= m;i++) {
        scanf("%d %d %d", &opt, &l, &r);
        Sort(l, r, opt);
    }
    scanf("%d", &q);
    printf("%d", Rank(q));
    return 0;
}
posted @ 2023-09-28 13:21  Saka_Noa  阅读(14)  评论(0)    收藏  举报  来源