洛谷P3380 【模板】二逼平衡樹(樹套樹)

題目描述

您須要寫一種數據結構(可參考題目標題),來維護一個有序數列,其中須要提供如下操做:node

  1. 查詢k在區間內的排名ios

  2. 查詢區間內排名爲k的值數據結構

  3. 修改某一位值上的數值ide

  4. 查詢k在區間內的前驅(前驅定義爲嚴格小於x,且最大的數,若不存在輸出-2147483647)ui

  5. 查詢k在區間內的後繼(後繼定義爲嚴格大於x,且最小的數,若不存在輸出2147483647)spa

注意上面兩條要求和tyvj或者bzoj不同,請注意

輸入輸出格式

輸入格式:code

 

第一行兩個數 n,m 表示長度爲n的有序序列和m個操做blog

第二行有n個數,表示有序序列get

下面有m行,opt表示操做標號string

若opt=1 則爲操做1,以後有三個數l,r,k 表示查詢k在區間[l,r]的排名

若opt=2 則爲操做2,以後有三個數l,r,k 表示查詢區間[l,r]內排名爲k的數

若opt=3 則爲操做3,以後有兩個數pos,k 表示將pos位置的數修改成k

若opt=4 則爲操做4,以後有三個數l,r,k 表示查詢區間[l,r]內k的前驅

若opt=5 則爲操做5,以後有三個數l,r,k 表示查詢區間[l,r]內k的後繼

 

輸出格式:

 

對於操做1,2,4,5各輸出一行,表示查詢結果

 

輸入輸出樣例

輸入樣例#1:  複製
9 6
4 2 2 1 9 4 0 1 1
2 1 4 3
3 4 10
2 1 4 3
1 2 5 9
4 3 9 5
5 2 8 5
輸出樣例#1:  複製
2
4
3
4
9

說明

時空限制:2s,128M

n,m \leq 5\cdot {10}^4n,m5104 保證有序序列全部值在任什麼時候刻知足 [0, {10} ^8][0,108]

題目來源:bzoj3196 / Tyvj1730 二逼平衡樹,在此鳴謝

此數據爲洛谷原創。(特別提醒:此數據不保證操做五、6必定存在,故請務必考慮不存在的狀況)

 

複習了一下樹套樹

感受很套路啊qwq,

然而不想重寫, 因此就對着之前的抄了一遍。

 

// luogu-judger-enable-o2
#include<iostream> #include<cstring> #include<cstdio> #include<algorithm>

using namespace std; const int MAXN = 2000001; inline int read() { char c = getchar();int x = 0,f = 1; while(c < '0' || c > '9'){if(c == '-')f = -1;c = getchar();} while(c >= '0' && c <= '9'){x = x * 10 + c - '0',c = getchar();} return x * f; } #define Ls(k) s[k].ch[0]
#define Rs(k) s[k].ch[1]
struct sp { int siz, ch[2], fa, rev, num; } s[MAXN]; int sz; inline void pushup(int k) { //上傳splay標記
    s[k].siz = s[s[k].ch[0]].siz + s[s[k].ch[1]].siz + s[k].rev; } inline int ident(int x) { // 判斷x是父親的哪一個兒子
    return s[s[x].fa].ch[1] == x; } inline void connect(int x, int f, int how) { s[x].fa = f; s[f].ch[how] = x; } inline void rotate(int &root,int x) { // 對x進行雙旋操做
    int Y = s[x].fa, R = s[Y].fa, Yson = ident(x), Rson = ident(Y); int B = s[x].ch[Yson ^ 1]; if(!R) root = x; connect(x, R, Rson); connect(Y, x, Yson ^ 1); connect(B, Y, Yson); pushup(Y); pushup(x); } inline void splay(int &root, int x, int to) { // tag
    while(s[x].fa != to) { int y = s[x].fa; if(s[y].fa == to) rotate(root, x); else if(ident(x) == ident(y)) rotate(root, y),rotate(root, x); else rotate(root, x),rotate(root, x); } } inline void insert(int &k, int c) { // k節點,插入值爲c的元素
    if(k == 0) { k=++sz; s[k].siz = s[k].rev = 1; s[k].num = c; return ; } if(s[k].num==c) s[k].rev++; else if(s[k].num < c) insert(Rs(k), c), s[Rs(k)].fa = k; else insert(Ls(k), c), s[Ls(k)].fa = k; pushup(k); } inline int getpre(int k,int val) { //小於val的最大值
    int pos = k, ret; while(pos) { if(s[pos].num >= val) pos = Ls(pos); else ret = pos, pos = Rs(pos); } return ret; } inline int getsuc(int k,int val) { int pos = k, ret; while(pos) { if(s[pos].num <= val) pos = s[pos].ch[1]; else ret = pos, pos = s[pos].ch[0]; } return ret; } inline int getk(int k,int val) { if(s[k].num == val)   return k; if(s[k].num < val)    return getk(Rs(k),val); if(s[k].num > val)    return getk(Ls(k),val); } #define ls k << 1
#define rs k << 1 | 1
struct node { int l, r, root, mx, mn; } T[MAXN]; inline void pushup_s(int k) { // 上傳線段樹的標記
    T[k].mx = max(T[ls].mx, T[rs].mx); T[k].mn = min(T[ls].mn, T[rs].mn); } inline void Build(int k,int l,int r) { //下標爲k,左端點爲l,右端點爲r
    T[k].l = l; T[k].r = r; if(l == r)    return ; int mid = (l + r) >> 1; Build(ls, l, mid); Build(rs, mid + 1, r);// 線段樹模板,沒啥好說的,
} inline void delet(int &k, int val) { //刪除值爲val的節點
    int x = getk(k,val);//獲得值爲val的編號
    if(s[x].rev > 1) { s[x].rev--; s[x].siz--; splay(k, x, 0); } else { int p = getpre(k, val),su = getsuc(k, val);// 找到前驅和後繼
        splay(k, p, 0); splay(k, su, p);// 把前驅旋轉到根節點,把後繼旋轉到根節點的孩子 
        Ls(su) = 0;// 刪除後繼的左孩子,表示沒有小於他的點,這樣就成功把x節點刪除 
 } } inline void build(int k, int pos, int x) { // 在下標爲k,位置爲pos的地方插入一個值爲x的元素 
    insert(T[k].root, x);//在線段樹root節點的splay中插入一個值爲x的元素
    if(T[k].l == T[k].r) { T[k].mx = x; T[k].mn = x; return ; } int mid = (T[k].l + T[k].r) >> 1; if(pos <= mid) build(ls, pos, x); if(pos > mid) build(rs, pos, x); pushup_s(k);//別忘了上傳線段樹標記 
} int NewNode(int val, int f) { s[++sz].rev = s[sz].siz = 1; s[sz].num = val; s[sz].fa = f; return sz; } inline void dfsseg(int k) { //對以k下標開始的線段樹進行遍歷
    int x = getsuc(T[k].root, -1), y = getpre(T[k].root, 1e8+1);//這樣計算出來的x和y必定知足:x是k號線段樹中的最小值的位置,y是k號線段樹中最大值的位置
    splay(T[k].root, x, 0);//將x旋轉到根
    s[x].siz++; s[x].ch[0] = NewNode(-1, x); splay(T[k].root, y, 0); s[y].siz++; s[y].ch[1] = NewNode(1e8 + 1, y); if(T[k].l == T[k].r)    return ; dfsseg(ls); dfsseg(rs);// 對於每個線段,增長兩個虛節點
} inline int getmax(int k,int l,int r) { //在l到r中找最大的元素
    if(l <= T[k].l && T[k].r <= r) return T[k].mx; int mid=(T[k].l + T[k].r) >> 1,ret = -1; if(l <= mid)    ret = max(ret, getmax(ls, l, r)); if(mid <  r)    ret = max(ret, getmax(rs, l, r)); return ret; } inline int getmin(int k,int l,int r) { //在l到r中找最小的元素
    if(l <= T[k].l && T[k].r <= r) return T[k].mn; int mid = (T[k].l + T[k].r) >> 1,ret = 1e8+1; if(l <= mid)    ret = min(ret, getmin(ls, l, r)); if(mid <  r)    ret = min(ret, getmin(rs, l, r)); return ret; } inline int query_order(int k, int l, int r, int val) { //下標爲k,查詢val在區間l到r中有多少比它小的數
    if(l <= T[k].l && T[k].r <= r) { int p = getpre(T[k].root, val); splay(T[k].root, p, 0); return s[p].siz - s[Rs(p)].siz - 1;//  } int mid = (T[k].l + T[k].r) >> 1, ret = 0; if(l <= mid)    ret += query_order(ls, l, r, val); if(r >  mid)    ret += query_order(rs, l, r, val); return ret; } inline void modify(int k,int pos,int pre,int now) { //在下標爲k的線段樹中的pos位置值爲pre的節點的值修改成now
    delet(T[k].root, pre);// 先把pre刪掉
    insert(T[k].root, now);// 再把now加上
    if(T[k].l==T[k].r) { T[k].mx = now; T[k].mn = now; return ; } int mid = (T[k].l + T[k].r) >> 1; if(pos <= mid) modify(ls , pos, pre, now); if(pos > mid) modify(rs , pos, pre, now); pushup_s(k);// 別忘了上傳標記!
} inline int query_pre(int k,int l,int r,int val) {//查詢區間l到r內val的前驅 
    if(l <= T[k].l && T[k].r <= r) return s[getpre(T[k].root,val)].num; int mid = (T[k].l + T[k].r) >> 1,ret = -1; if(l <= mid)    ret = max(ret, query_pre(ls, l, r, val)); if(mid < r)        ret = max(ret, query_pre(rs, l, r, val)); return ret; } inline int query_suc(int k,int l,int r,int val) {//查詢區間l到r內val的後繼 
    if(l <= T[k].l && T[k].r <= r) return s[getsuc(T[k].root,val)].num; int mid = (T[k].l + T[k].r) >> 1, ret = 1e8 + 1; if(l <= mid)    ret = min(ret, query_suc(ls, l, r, val)); if(mid <  r)    ret = min(ret, query_suc(rs, l, r, val)); return ret; } inline int QueryNum(int L,int R,int val) { // 在L到R的區間中查找val的排名
    int l = 1, r = getmax(1, L, R), ret, tmp; while(l <= r) { //二分答案
        int mid = (l + r) >> 1; tmp = query_order(1, L, R, mid); if(tmp < val) ret = mid, l = mid + 1; else r = mid - 1; } return ret; } int n, m; int date[MAXN]; int main() { #ifdef WIN32 freopen("a.in", "r", stdin); #endif n = read(); m = read(); Build(1, 1, n);//建好線段樹
    for(int i = 1; i <= n; i++) date[i] = read(); //讀入初始數據
    for(int i = 1; i <= n; i++) build(1, i, date[i]);//把每個元素都插到線段樹裏面去
    dfsseg(1);// 把線段樹的全部節點增長兩個虛節點
    while(m--) { int l, r, k, pos, opt; opt = read(); if(opt == 1) { //查詢k在l到r中的排名
            l = read(); r = read(); k = read(); printf("%d\n",query_order(1, l, r, k) + 1); } if(opt == 2) { // 查詢排名爲k的值
            l = read(); r = read(); k = read(); printf("%d\n", QueryNum(l, r, k)); } if(opt==3) { // 將pos位置的數修改成k
            pos = read(); k = read(); modify(1, pos, date[pos], k); date[pos] = k;//順便修改date的值
 } if(opt==4) { l = read(); r = read(); k = read(); int tmp = query_pre(1, l, r, k);// 查詢tmp的前驅
            if(tmp != -1) printf("%d\n", tmp); else printf("-2147483647\n"); } if(opt == 5) { l = read(); r = read(); k = read(); int tmp = query_suc(1,l,r,k); if(tmp != 1e8 + 1) printf("%d\n", tmp); else printf("2147483647\n"); } } return 0; }
相關文章
相關標籤/搜索