[bzoj4552][Tjoi2016&Heoi2016]排序【线段树】

【题目链接】
  https://www.lydsy.com/JudgeOnline/problem.php?id=4552
【题解】
  线段树的一些特殊技巧:合并与分裂。
  首先把每个点都建出一棵权值线段树,每次排序时,将包含的权值线段树合并在一起即可。可以用set维护当前的线段树集合。
  但是排序可能会将两端的线段树分开,这时就要进行分裂操作,形象一点,就是将线段树从中间切开,在切割的地方新增一条节点,然后原来的分给左边,新的给右边。
  由于线段树合并的总复杂度是 O() O ( 节 点 个 数 ) ,每次分裂最多增加 O(2logN) O ( 2 l o g N ) 个节点,所以总复杂度是 O(NlogN) O ( N ∗ l o g N )
【代码】

/* - - - - - - - - - - - - - - -
    User :      VanishD
    problem :   [bzoj4552]
    Points :    segment tree
- - - - - - - - - - - - - - - */
# include 
# define    ll      long long
# define    inf     0x3f3f3f3f
# define    N       100010
# define    K       50
using namespace std;
typedef pair <int, int> pr;
int read(){
    int tmp = 0, fh = 1; char ch = getchar();
    while (ch < '0' || ch > '9'){ if (ch == '-') fh = -1; ch = getchar(); }
    while (ch >= '0' && ch <= '9'){ tmp = tmp * 10 + ch - '0'; ch = getchar(); }
    return tmp * fh;
}
struct Tree{
    int pl, pr, num;
}T[N * K];
set  mp;
int rt[N], n , m, place, tag[N];
int extend(int p, int num, int l, int r){
    if (!p) p = ++place;
    T[p].num++;
    if (l != r){
        int mid = (l + r) / 2;
        if (mid >= num) 
            T[p].pl = extend(T[p].pl, num, l, mid);
            else T[p].pr = extend(T[p].pr, num, mid + 1, r);
    }
    return p;
}
void splitmn(int &pl, int &pr, int num, int l, int r){
    pr = ++place;
    if (l == r){
        T[pr].num = T[pl].num - num;
        T[pl].num = num;
        return;
    }
    if (T[T[pl].pl].num == num){
        T[pr].pr = T[pl].pr, T[pl].pr = 0;
    }
    else {
        int mid = (l + r) / 2;
        if (T[T[pl].pl].num < num)
            splitmn(T[pl].pr, T[pr].pr, num - T[T[pl].pl].num, mid + 1, r);
        else {
            T[pr].pr = T[pl].pr; T[pl].pr = 0;
            splitmn(T[pl].pl, T[pr].pl, num, l, mid);
        }
    }
    T[pl].num = T[T[pl].pl].num + T[T[pl].pr].num;
    T[pr].num = T[T[pr].pl].num + T[T[pr].pr].num;
}
void splitmx(int &pl, int &pr, int num, int l, int r){
    pr = ++place;
    if (l == r){
        T[pr].num = T[pl].num - num;
        T[pl].num = num;
        return;
    }
    if (T[T[pl].pr].num == num){
        T[pr].pl = T[pl].pl, T[pl].pl = 0;
    }
    else {
        int mid = (l + r) / 2;
        if (T[T[pl].pr].num < num)
            splitmx(T[pl].pl, T[pr].pl, num - T[T[pl].pr].num, l, mid);
        else {
            T[pr].pl = T[pl].pl; T[pl].pl = 0;
            splitmx(T[pl].pr, T[pr].pr, num, mid + 1, r);
        }
    }
    T[pl].num = T[T[pl].pl].num + T[T[pl].pr].num;
    T[pr].num = T[T[pr].pl].num + T[T[pr].pr].num;
}
int merge(int pl, int pr, int l, int r){
    if (pl == 0) return pr;
    if (pr == 0) return pl;
    T[pl].num = T[pl].num + T[pr].num;
    if (l != r){
        int mid = (l + r) / 2;
        T[pl].pl = merge(T[pl].pl, T[pr].pl, l, mid);
        T[pl].pr = merge(T[pl].pr, T[pr].pr, mid + 1, r);
    }
    return pl;
}
int query(int p, int num, int l, int r){
    if (l == r) return l;
    int mid = (l + r) / 2;
    if (T[T[p].pl].num >= num) return query(T[p].pl, num, l, mid);
        else return query(T[p].pr, num - T[T[p].pl].num, mid + 1, r);
}
int main(){
    freopen("bzoj4552.in", "r", stdin);
    freopen("bzoj4552.out", "w", stdout);
    n = read(), m = read();
    int L = 1, R = n;
    for (int i = 1; i <= n; i++){
        int x = read();
        rt[i] = extend(rt[i], x, L, R);
        tag[i] = 0;
        mp.insert(make_pair(i, i));
    }
    for (int i = 1; i <= m; i++){
        int op = read(), l = read(), r = read(), thl, thr;
        set  :: iterator it = mp.lower_bound(make_pair(l, 0));
        if (it == mp.end() || (*it).first > l) it--;
        thl = (*it).first, thr = (*it).second;
        if (thl != l){
            if (tag[thl] == 0){
                splitmn(rt[thl], rt[l], (l - 1) - thl + 1, L, R);
                tag[l] = 0;
            }
            else {
                splitmx(rt[thl], rt[l], (l - 1) - thl + 1, L, R);
                tag[l] = 1;
            }
            mp.erase(it);
            mp.insert(make_pair(thl, l - 1));
            mp.insert(make_pair(l, thr));
        }
        it = mp.lower_bound(make_pair(r, 0));
        if (it == mp.end() || (*it).first > r) it--;
        thl = (*it).first, thr = (*it).second;
        if (r != thr){
            if (tag[thl] == 0){
                splitmn(rt[thl], rt[r + 1], r - thl + 1, L, R);
                tag[r + 1] = 0;
            }
            else {
                splitmx(rt[thl], rt[r + 1], r - thl + 1, L, R);
                tag[r + 1] = 1;
            }
            mp.erase(it);
            mp.insert(make_pair(thl, r));
            mp.insert(make_pair(r + 1, thr));
        }
        it = mp.lower_bound(make_pair(l, 0));
        if (it == mp.end() || (*it).first > l) it--;
        int now = (*it).second + 1, nex;
        mp.erase(it);
        for (; now <= r; now = nex + 1){
            it = mp.lower_bound(make_pair(now, 0));
            nex = (*it).second;
            rt[l] = merge(rt[l], rt[now], L, R);
            rt[now] = 0;
            mp.erase(it);
        }
        tag[l] = op;
        mp.insert(make_pair(l, r));
    }
    int q = read();
    set  :: iterator it = mp.begin();
    while ((*it).second - (*it).first + 1 < q){
        q -= (*it).second - (*it).first + 1;
        it++;
    } 
    if (tag[(*it).first] == 1)
        q = (*it).second - (*it).first + 1 - q + 1;
    printf("%d\n", query(rt[(*it).first], q, L, R));
    return 0;
}

你可能感兴趣的:(【线段树】)