[BZOJ1901][ZJU2112]Dynamic Rankings

Zju2112 Dynamic Rankings

Description
给定一个含有n个数的序列a[1],a[2],a[3]……a[n],程序必须回答这样的询问:对于给定的i,j,k,在a[i],a[i+1],a[i+2]……a[j]中第k小的数是多少(1≤k≤j-i+1),并且,你可以改变一些a[i]的值,改变后,程序还能针对改变后的a继续回答上面的问题。你需要编一个这样的程序,从输入文件中读入序列a,然后读入一系列的指令,包括询问指令和修改指令。对于每一个询问指令,你必须输出正确的回答。 第一行有两个正整数n(1≤n≤10000),m(1≤m≤10000)。分别表示序列的长度和指令的个数。第二行有n个数,表示a[1],a[2]……a[n],这些数都小于10^9。接下来的m行描述每条指令,每行的格式是下面两种格式中的一种。 Q i j k 或者 C i t Q i j k (i,j,k是数字,1≤i≤j≤n, 1≤k≤j-i+1)表示询问指令,询问a[i],a[i+1]……a[j]中第k小的数。C i t (1≤i≤n,0≤t≤10^9)表示把a[i]改变成为t。
Input
对于每一次询问,你都需要输出他的答案,每一个输出占单独的一行。
Output
Sample Input
5 3
3 2 1 4 7
Q 1 4 3
C 2 6
Q 2 5 3
Sample Output
3
6
HINT
20%的数据中,m,n≤100; 40%的数据中,m,n≤1000; 100%的数据中,m,n≤10000。

Solution :
简单来说就是带修改的区间第k大.
不带修改的时候我们有一个经典做法,就是利用可持久化线段树,对于每一个位置都建立一棵保存[1,i]的线段树,利用区间减法得到区间信息.
其实我们可以知道,上面这种做法其实就是做了一个前缀和,那么这里我们就是要维护前缀和.
对于一个数列来说维护前缀和很简单,通过树状数组实现即可,其实对于线段树,可以看成是很多数列构成的,我们同样可以利用树状数组维护.

Code

#include <bits/stdc++.h>
using namespace std;

#define rep(i, l, r) for (int i = (l); i <= (r); i++)
#define per(i, r, l) for (int i = (r); i >= (l); i--)
#define MS(_) memset(_, 0, sizeof(_))
#define MP make_pair
#define PB push_back
typedef long long ll;
typedef pair<int, int> PII;
template<typename T> inline void read(T &x){
    x = 0; T f = 1; char ch = getchar();
    while (!isdigit(ch)) {if (ch == '-') f = -1; ch = getchar();}
    while (isdigit(ch))  {x = x * 10 + ch - '0'; ch = getchar();}
    x *= f;
}

const int N = 10000 + 10; 
int n, m, top = 0, sz = 0, La, Lb, tot;
int num[N<<1], v[N], A[N], B[N], K[N], flag[N];
int root[N], lc[N*100], rc[N*100], sum[N*100], L[N], R[N];

inline int lowbit(int x) { return x&-x; }
inline void modify(int l, int r, int &x, int pos, int delta){
    if (!x) x = ++sz; sum[x] += delta;
    if (l == r) return;
    int mid = l+r >> 1;
    if (pos <= mid) modify(l, mid, lc[x], pos, delta);
    else modify(mid+1, r, rc[x], pos, delta);
}
inline int query(int l, int r, int k){
    if (l == r) return l;
    int suml = 0, sumr = 0;
    rep(i, 1, La) suml += sum[lc[L[i]]]; rep(i, 1, Lb) sumr += sum[lc[R[i]]];
    int mid = (l+r)>>1;
    if (k <= sumr - suml){
        rep(i, 1, La) L[i] = lc[L[i]]; rep(i, 1, Lb) R[i] = lc[R[i]];
        return query(l, mid, k);    
    }else{
        rep(i, 1, La) L[i] = rc[L[i]]; rep(i, 1, Lb) R[i] = rc[R[i]];
        return query(mid+1, r, k-(sumr-suml));
    }
}
int main(){
    read(n); read(m);
    rep(i, 1, n) scanf("%d", &v[i]), num[++top] = v[i];
    rep(i, 1, m){ char ch[3];
        scanf("%s%d%d", ch, &A[i], &B[i]);
        if (ch[0] == 'Q') { scanf("%d", &K[i]); flag[i] = 1; }
        else num[++top] = B[i];
    }
    sort(num+1, num+1+top);
    tot = unique(num+1, num+1+top)-num-1;

    rep(i, 1, n){
        int pos = upper_bound(num+1, num+1+tot, v[i])-num-1;
        for (int j = pos; j <= n; j += lowbit(j)) modify(1, tot, root[j], pos, 1);
    }

    rep(i, 1, m) if (flag[i]){
        La = Lb = 0; A[i]--;
        for (int j = A[i]; j; j -= lowbit(j)) L[++La] = root[j];
        for (int j = B[i]; j; j -= lowbit(j)) R[++Lb] = root[j];
        printf("%d\n", num[query(1, tot, K[i])]);
    }else{
        int pos = upper_bound(num+1, num+1+tot, v[A[i]])-num-1;
        for (int j = A[i]; j <= n; j += lowbit(j)) modify(1, tot, root[j], pos, -1);
        v[A[i]] = B[i];
        pos = upper_bound(num+1, num+1+tot, v[A[i]])-num-1;
        for (int j = A[i]; j <= n; j += lowbit(j)) modify(1, tot, root[j], pos, 1);        
    }

    return 0;
}

你可能感兴趣的:(可持久化线段树)