ZOJ 2112 动态区间第K大(二分答案+线段树套Treap)

来源:互联网 发布:origin软件百度云 编辑:程序博客网 时间:2024/05/17 22:51

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
(adviser)
Site: http://zhuzeyuan.hp.infoseek.co.jp/index.files/our_contest_20040619.htm


        人生第一道树套树?

        之所以打问号,是因为其实之前写二维线段树的时候,那个也可以相当于是线段树套线段树,所以严格意义来说这个不是第一个道题……

        普通的区间第k大很简单,各种数据结构乱搞就行了,但是如果要支持修改的话就一定要树套树。之所以用了Treap,就是想多熟悉一下,毕竟之前平衡树用的太少了。然后具体思路也是很简单,外面虚建一棵线段树,作用就是把区间分段,分段的同时对于每一个区间都建立一棵Treap,用来维护子区间的大小关系。由于Treap支持删除和插入操作,所以相当于可以进行修改。这里,不明白树套树的要注意,每次插入和删除一个点,不仅仅是改变一个Treap,而是每一个包含该点的区间对应的Treap都要修改,可以类似参照之前写的二维线段树模板。

        然后,虽然这么做了,但是这本身是不支持区间合并的,也就是说当问区间[l,r]的第k大的时候,如果之前分区间的时候,没有恰好分到这个区间,我们是无法直接输出结果的。所以说,我们得用二分答案。每次二分一个数字,在对应区间中找比他小的数字的个数,如果比他小的数字恰好为k,那么结果就一定大于等于这个数字。如此二分下去就可以得到结果。具体见代码:

#include<bits/stdc++.h>#define N 1000010using namespace std;int n,m,Rnk,a[N];struct Treap{    struct treap    {        int son[2],size,num,cnt,fix;    } tree[N];    int sz,root[N];    inline void init()    {        sz=0;        memset(tree,0,sizeof(tree));        memset(root,0,sizeof(root));    }    inline void update(int x)    {        if (!x) return;        tree[x].size=tree[x].cnt;        if (tree[x].son[0]) tree[x].size+=tree[tree[x].son[0]].size;        if (tree[x].son[1]) tree[x].size+=tree[tree[x].son[1]].size;    }    inline void Rotate(int &x,bool ch)    {        int y=tree[x].son[ch^1],z=x;        tree[x].son[ch^1]=tree[y].son[ch];        tree[y].son[ch]=x; x=y;        update(z); update(y);    }    inline void ins(int &i,int x)    {        if (!i)        {            i=++sz;            tree[i].num=x;            tree[i].fix=rand();            tree[i].size=tree[i].cnt=1;            tree[i].son[0]=tree[i].son[1]=0;            return;        }        tree[i].size++;        if (tree[i].num==x) tree[i].cnt++;        else        {            bool ch=(x>tree[i].num);            ins(tree[i].son[ch],x);            if (tree[tree[i].son[ch]].fix>tree[i].fix) Rotate(i,ch^1);        }        update(i);    }    inline void del(int &i,int x)    {        if (!i) return;        if (tree[i].num==x)        {            if (tree[i].cnt>1)            {                tree[i].size--;                tree[i].cnt--;                return;            }            if (tree[i].son[0]*tree[i].son[1]==0) i=tree[i].son[1]+tree[i].son[0];            else            {                bool ch=(tree[tree[i].son[0]].fix>tree[i].fix);                Rotate(i,ch); del(tree[i].son[ch],x);            }        }        else if (x<tree[i].num)        {            del(tree[i].son[0],x);            tree[i].size--;        } else        {            del(tree[i].son[1],x);            tree[i].size--;        }        if (i) update(i);    }    inline int getrank(int &i,int x)    {        int res=0;        if (!i) return res;        if (x>=tree[i].num)        {            res+=tree[i].cnt+getrank(tree[i].son[1],x);            if (tree[i].son[0]) res+=tree[tree[i].son[0]].size;        } else res=getrank(tree[i].son[0],x);        return res;    }} treap;inline void ins(int i,int l,int r,int x,int num){    treap.ins(treap.root[i],num);    if (l==r) return;    int mid=(l+r)>>1;    if (mid>=x) ins(i<<1,l,mid,x,num);           else ins(i<<1|1,mid+1,r,x,num);}inline void update(int i,int l,int r,int x,int his,int now){    treap.del(treap.root[i],his);    treap.ins(treap.root[i],now);    if (l==r) return;    int mid=(l+r)>>1;    if (mid>=x) update(i<<1,l,mid,x,his,now);           else update(i<<1|1,mid+1,r,x,his,now);}inline void getrank(int i,int l,int r,int L,int R,int x){    if (l==L&&r==R)    {        Rnk+=treap.getrank(treap.root[i],x);        return;    }    int mid=(l+r)>>1;    if (mid>=R) getrank(i<<1,l,mid,L,R,x);    else if (mid<L) getrank(i<<1|1,mid+1,r,L,R,x);    else    {        getrank(i<<1,l,mid,L,mid,x);        getrank(i<<1|1,mid+1,r,mid+1,R,x);    }}int main(){    int T_T;    cin>>T_T;    srand(time(NULL));    while(T_T--)    {        scanf("%d%d",&n,&m);        treap.init();        for(int i=1;i<=n;i++)        {            scanf("%d",&a[i]);            ins(1,1,n,i,a[i]);        }        char op[10];        for(int i=1;i<=m;i++)        {            scanf("%s",op);            if (op[0]=='C')            {                int x,y; scanf("%d%d",&x,&y);                update(1,1,n,x,a[x],y); a[x]=y;            } else            {                int L,R,k; scanf("%d%d%d",&L,&R,&k);                int l=0,r=1e9;                while(l<=r)                {                    int mid=(l+r)>>1; Rnk=0;                    getrank(1,1,n,L,R,mid);                    if (Rnk>=k) r=mid-1; else l=mid+1;                }                printf("%d\n",l);            }        }    }    return 0;}

阅读全文
0 0
原创粉丝点击