zoj-2112-Dynamic Rankings主席树模板

来源:互联网 发布:对社区网络式管理创新 编辑:程序博客网 时间:2024/06/05 10:57
Dynamic Rankings

Time Limit: 10 Seconds      Memory Limit: 32768 KB

The Company Dynamic Rankings has developed a new kind of computer that is no longer satisfied with the query like to simply find the k-th smallest number of the given N numbers. They have developed a more powerful system such that for N numbers a[1], a[2], ..., a[N], you can ask it like: what is the k-th smallest number of a[i], a[i+1], ..., a[j]? (For some i<=j, 0<k<=j+1-i that you have given to it). More powerful, you can even change the value of some a[i], and continue to query, all the same.

Your task is to write a program for this computer, which

- Reads N numbers from the input (1 <= N <= 50,000)

- Processes M instructions of the input (1 <= M <= 10,000). These instructions include querying the k-th smallest number of a[i], a[i+1], ..., a[j] and change some a[i] to t.


Input

The first line of the input is a single number X (0 < X <= 4), the number of the test cases of the input. Then X blocks each represent a single test case.

The first line of each block contains two integers N and M, representing N numbers and M instruction. It is followed by N lines. The (i+1)-th line represents the number a[i]. Then M lines that is in the following format

Q i j k or
C i t

It represents to query the k-th number of a[i], a[i+1], ..., a[j] and change some a[i] to t, respectively. It is guaranteed that at any time of the operation. Any number a[i] is a non-negative integer that is less than 1,000,000,000.

There're NO breakline between two continuous test cases.


Output

For each querying operation, output one integer to represent the result. (i.e. the k-th smallest number of a[i], a[i+1],..., a[j])

There're NO breakline between two continuous test cases.


Sample Input

2
5 3
3 2 1 4 7
Q 1 4 3
C 2 6
Q 2 5 3
5 3
3 2 1 4 7
Q 1 4 3
C 2 6
Q 2 5 3


Sample Output

3
6
3
6


总算是A了第一道主席树了,主席树的总体思想就是用n颗线段树(n是数组长度,并且这n颗线段树的结构是一模一样的,都是0-num)去维护一个前缀和,对于没有修改的主席树只需要维护一个T[i] 就够了,其中T[i] 表示第i颗线段树  当前已经插入了i个数,线段树的总共num个节点,(num是数组里面的数加上修改后的数去重以后得到的长度,由于数很大,要把所有的数哈希),线段树维护的是该区间数的个数,因为线段树里面的叶子节点是从小到大排序的,所有要查找第k大的数的时候若线段树左子树数的总数小于k那么第k大的一定在右子树,然后k-=左子树数的总数查找右子树,否则就在左子树查找左子树。

现在要查找的是在l到r的第k大的数,因为第i颗线段树表示当前这颗线段树已经插入了1至i的这些数,因为l至r区间应该是一颗只插入了第l至r的数的线段树,而这颗线段树可以用

T[r] - T[l-1]表示,于是这颗树的左子树的数的总数就等于第r颗树的左子树的数的总数减去第l-1颗树的左子树的数的总数。

上述说的只是没有修改的主席树,那么如果要修改节点的话,就要用树状数组维护第1到第i颗树的数的个数总和,比如说现在要把数组中第i个位置的修改为t(假设t在num个数里面的位置为vis,原来的a[i]在numge数里面的位置是vis1)那么树状数组中从第i颗树开始到第n个数的所有线段树的所有包含vis1的段全部减1,所有包含vis的段全部加1,因为主席树是一种不修改原来结构的数据结构,它每次修改一个数的时候,相当于直接添加了一颗新的树原来的树都不变,所以在求l到r第k大的数的时候,直接还是用上述的方法求出T[r] - T[l-1]再加上 S[r] - s[l-1]。

主席树建的n颗线段树并不是每建一颗树就开一颗树那么大的空间,而是重复利用之前的树,主席树一开始是建一颗叶子节点为0-num的一颗空树T[0],当你建T[1] 的时候,首先 先为T[1]建一个跟节点,如果当前要插入的数在左区间,那么为左区间建一个节点,右区间等于T[0]的右区间,然后继续往下,一直到跟节点,反正就是当前要插入左区间,就为左区间建一个节点,右区间等于上一颗树的右区间,否则 为右区间建一个节点,左区间等于上一颗树的左区间。

#include <cstdio>#include <cmath>#include <algorithm>#include <iostream>#include <cstring>#include <queue>using namespace std;const int maxn = 60005;int T[maxn]; //主席树的n个节点int S[maxn]; //树状数组维护主席树的前缀和int a[maxn],b[maxn];//离线哈希int tot;int num;int use[maxn];//求前缀和时保存当前线段树的要查询区间的根节点int lson[2500000],rson[2500000],c[2500000];//保存每颗线段树的左右节点,和当前节点数的个数int n,m;void hase(int k) //离线哈希{    sort(b,b+k);    num = unique(b,b+k) - b;}int get_hase(int now) // 获取数的位置{    return lower_bound(b,b+num,now) - b;}struct Q{    int l,r,w,kind;}q[10005];//保存查询int build(int l,int r){    int root = tot++;    c[root] = 0;    if(l != r)    {        int mid = (l + r) >> 1;        lson[root] = build(l,mid);        rson[root] = build(mid+1,r);    }    return root;}//建树int insert1(int root,int pos,int val) // 创建一个新线段树,之前的继续保留下来{    int newroot = tot++;    int tmp = newroot;    c[newroot] = c[root] + val;    int l = 0,r = num - 1;    while(l < r)    {        int mid = (l + r) >> 1;        if(mid >= pos)        {            lson[newroot] = tot++;            rson[newroot] = rson[root];            newroot  = lson[newroot];            root = lson[root];            r = mid;        }        else        {            lson[newroot] = lson[root];            rson[newroot] = tot++;            newroot = rson[newroot];            root = rson[root];            l = mid + 1;        }        c[newroot] = c[root] + val;    }    return tmp;}void add(int x,int pos,int val) //更新树状数组{    while(x <= n)    {        S[x] = insert1(S[x],pos,val);        x += x&(-x);    }}int sum(int x){    int ret = 0;    while(x > 0)    {        ret += c[lson[use[x]]];        x -= (x & (-x));    }    return ret;}//计算前缀和int qurry(int ll,int rr,int pos) {    int i;    int rootl = T[ll];    int rootr = T[rr];    for(i = ll; i > 0 ; i-= (i & (-i))) use[i] = S[i];    for(i = rr; i > 0 ; i -= (i&(-i))) use[i] = S[i];    int l = 0,r = num - 1;    while(l < r)    {        int mid = (l + r) >> 1;        int cou = sum(rr) -sum(ll) + c[lson[rootr]] - c[lson[rootl]];       // printf("cou = %d\n",cou);        if(cou < pos)        {            pos -= cou;            for(i = ll; i > 0; i -= (i & (-i))) use[i] = rson[use[i]];            for(i = rr; i > 0; i -= (i & (-i))) use[i] = rson[use[i]];            rootl = rson[rootl];            rootr = rson[rootr];            l = mid + 1;        }        else        {            for(i = ll; i > 0; i -= i & (-i)) use[i] = lson[use[i]];            for(i = rr; i > 0; i -= i & (-i)) use[i] = lson[use[i]];            rootl = lson[rootl];            rootr = lson[rootr];            r = mid;        }    }    return l;}int main(){    int t;    scanf("%d",&t);    while(t--)    {        scanf("%d %d",&n,&m);        int i,j;        num = tot = 0;        for(i = 0; i < n; i++)        {            scanf("%d",&a[i]);            b[num++] = a[i];        }        char str[10];        for(i = 0; i < m; i++)        {            scanf("%s %d %d",str,&q[i].l,&q[i].r);            if(str[0] == 'Q')            {                q[i].kind = 0;                scanf("%d",&q[i].w);            }            else            {                q[i].kind = 1;                b[num++] = q[i].r;            }        }        hase(num);        T[0] = build(0,num-1);        for(i = 1; i <= n; i++)        {            T[i] = insert1(T[i-1],get_hase(a[i-1]),1);        }        for(i = 1; i <= n; i++)            S[i] = T[0];        for(i = 0; i < m; i++)        {            if(q[i].kind == 0)            {                printf("%d\n",b[qurry(q[i].l - 1,q[i].r,q[i].w)]);            }            else            {                int v = get_hase(q[i].r);                add(q[i].l,get_hase(a[q[i].l-1]),-1);                add(q[i].l,v,1);                a[q[i].l - 1] = q[i].r;            }        }    }    return 0;}



0 0
原创粉丝点击