[树套树] 可持久化线段树 树状数组套值域线段树

大家都很强, 可与之共勉。

q l, r, k

查询[l, r] 第k小。

m pos, x;
把pos位置的改为x。

每次询问就是对O(logn) 棵树加加减减后求第k 大。修改也会同时修改O(logn) 棵树,所以总的复杂度是:O((m+n)log^2 n)。

#include "cstdio"
#include "cctype"
#include "cstdlib"
#include "cstring"

#define lowbit(x)  (x & (-x))
#define min(a, b) ((a) < (b) ? (a) : (b))

#define rep(i, m, n)  for(register int i = (m); i <= (n); ++i)

template <class T>
inline bool readIn(T &x)  {
    T flag = 1;  char ch;
    while(!(isdigit(ch = (char) getchar())) && ch != EOF)  if( ch == '-' )  flag = -1;
    if(ch == EOF)  return false;
    for(x = ch - 48; isdigit(ch = (char) getchar()); x = (x << 1) + (x << 3) + ch - 48);
    x *= flag;
    return true;
}

template <class T>
inline void write(T x)  {
     if (x > 9)
     write(x / 10);
     putchar(x % 10 + 48);
}

template <class T>
inline bool writeIn(T x)  {
     if (x < 0)  {
         putchar('-');
         x = -x;
     }
     write(x);
     return true;
}

class io  {
public:
    io()  {
        freopen("intkth.in", "r", stdin);
        freopen("intkth.out", "w", stdout);
    }
    ~io()  {
        fclose(stdin);
        fclose(stdout);
    }   
} M;

const int MAXN = 100005;

struct Node  {
    int cnt;
    Node *ls, *rs;
    Node(int cnt = 0, Node *ls = NULL, Node *rs = NULL) : cnt(cnt), ls(ls), rs(rs)  {   }
}  pool[MAXN * 200], *tail = pool, *roots[MAXN], *null;

int ca, cb, n, m, l, r, k, pos, val, a[MAXN];
Node *va[10001], *vb[10001];

inline void init()  {
    null = ++tail;
    null -> ls = null;
    null -> rs = null;
    null -> cnt = 0;
}

inline Node *newnode()  {
    Node *nd = ++tail;
    nd->ls = null;
    nd->rs = null;
    nd->cnt = 0;
    return nd;
}

void modify( int lf, int rg, int pos, int delta )  {
    for(register int i = 0; i < ca; va[i]->cnt += delta, ++i);
    if( lf ^ rg )  {
        int mid = (lf + rg) >> 1;
        if( pos <= mid )  {
            for( int t = 0; t < ca; t++ )  {
                if( va[t]->ls == null ) 
                    va[t]->ls = newnode();
                va[t] = va[t]->ls;
            }
            modify( lf, mid, pos, delta );
        }  else  {
             for(register int i = 0; i < ca; ++i)  {
                 if( va[i] -> rs == null )
                     va[i] -> rs = newnode();
                 va[i] = va[i] -> rs;
             }
             modify( mid + 1, rg, pos, delta );
           }
    }
}

int query_seg( int lf, int rg, int k )  {
    if( lf == rg ) return lf;
    int mid = (lf + rg) >> 1;
    int lz = 0;
    for(register int i = 0; i < ca; ++i) lz += va[i] -> ls -> cnt;
    for(register int i = 0; i < cb; ++i) lz -= vb[i] -> ls -> cnt;
    if( k <= lz )  {
        for(register int i = 0; i < ca; ++i) va[i] = va[i]->ls;
        for(register int i = 0; i < cb; ++i) vb[i] = vb[i]->ls;
        return query_seg( lf, mid, k );
    }  else  {
        for(register int i = 0; i < ca; ++i) va[i] = va[i] -> rs;
        for(register int i = 0; i < cb; ++i) vb[i] = vb[i] -> rs;
        k -= lz;
        return query_seg( mid + 1, rg, k );
    }
}

inline void modify( int u, int x, int delta )  {
    ca = 0;
    for( register int i = u; i <= n; i += lowbit(i) ) 
        va[ca++] = roots[i];
    modify( 1, n, x, delta );
}

inline void modify( int u, int x )  {
    modify( u, a[u], -1 );
    modify( u, x, +1 );
    a[u] = x;
}

inline int query( int l, int r, int k )  {
    ca = cb = 0;
    for( register int i = r; i; i -= lowbit(i) )
        va[ca++] = roots[i];
    for( register int i = l - 1; i; i -= lowbit(i) )
        vb[cb++] = roots[i];
    return query_seg(1, n, k);
}

int main() {
    readIn(n);readIn(m);
    rep(i, 1, n)  readIn(a[i]);
    init();
    rep(i, 1, n)  roots[i] = newnode();
    rep(i, 1, n)  modify( i, a[i], +1 );
    while( m-- )  {
        static char s;
        while((s = (char) getchar()) == ' ' || s == '\n');
        if( s == 'q' )  {
            readIn(l);readIn(r);readIn(k);
            writeIn(query(l, r, k));  putchar(10);
        }  else  {
            readIn(pos);readIn(val);
            modify( pos, val );
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值