Live2d Test Env

ZOJ2112 Dynamic Rankings (线段树套平衡树)(主席树)

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.

 


<b< dd="">

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

 

第一次写树套树,而且应该是最基础的树套树,手抄大佬函数,自己整理成结构体。

没看别人题解的时候再高数课上YY,以为是对于线段树的每个节点套一个平衡树,这样的话想了一下,开的空间大概是n*lgn*n,基本上是炸了的。

 但是好像没有必要每个节点建立一个Treap,每个Treap给n个节点。因为过于浪费。

大佬们好像是只建立一个大Treap,但是有n个root,然后把空间分配到小Treap里面。

但是这样空间开n*lgn*lgn,好像1000000的空间还是不够处理极限情况啊。。。不知道这么回事。

 copy from qscqesze 顾学姐?结合前两天写的线段树和Treap,我们两个的风格还是比较像的,所以我理解代码起来也比较快。ORZ。。。对于空间的问题,多做几个题再回头分析。

#include<iostream>
#include<stdio.h>
#include<algorithm>
#include<cstring>
#include<ctime>
using namespace std;
#define maxn 1000001
int tmp=0;
struct Treap
{
    int root[maxn],sz,s[maxn],ch[maxn][2],v[maxn],w[maxn],rnd[maxn];//s是size,w是cnt 
    //rnd小根堆 
    void init()
    {
        memset(root,0,sizeof(root));
        sz=0;
    }
    void Update(int now)
    {
        s[now]=s[ch[now][0]]+s[ch[now][1]]+w[now];
    }
    void rotate(int &now,int p)
    {
        int t=ch[now][p];
        ch[now][p]=ch[t][1-p],ch[t][1-p]=now,s[t]=s[now];
        Update(now);now=t;//好像明白了为什么要替代:k得到儿子信息 
    }
    void Insert(int &now,int num)
    {
        if(!now){
            now=++sz;s[now]=w[now]=1;ch[now][0]=ch[now][1]=0;rnd[now]=rand();
            v[now]=num;return;
        }
        s[now]++;
        if(v[now]==num)w[now]++;
        else{
            int t=num>v[now];
            Insert(ch[now][t],num);
            if(rnd[ch[now][t]]<rnd[now])
                rotate(now,t);
        }
    }
    void Del(int &now,int num)//code better than mine before
    {
        if(v[now]==num){
            if(w[now]>1){
                w[now]--;
                s[now]--;
                return;
            }
            if(ch[now][0]*ch[now][1]==0)
                now=ch[now][0]+ch[now][1];
            else  rotate(now,rnd[ch[now][0]]>rnd[ch[now][1]]),Del(now,num);
        }
        else {
            Del(ch[now][num>v[now]],num);
            s[now]--;
        }
    }
    void Find(int now,int num)
    {
        if(!now) return;
        if(v[now]<=num){
            tmp+=s[ch[now][0]]+w[now];
            Find(ch[now][1],num);
        }
        else Find(ch[now][0],num);
    }
}Tr;
/////////////////////线段树
void Seg_insert(int now,int l,int r,int x,int num)
{
    Tr.Insert(Tr.root[now],num);
    if(l==r)return;
    int mid=(l+r)>>1;
    if(x<=mid)Seg_insert(now<<1,l,mid,x,num);
    else Seg_insert(now<<1|1,mid+1,r,x,num);
}

void Seg_change(int now,int l,int r,int x,int Now,int Pre)
{
    Tr.Del(Tr.root[now],Pre);
    Tr.Insert(Tr.root[now],Now);
    if(l==r)return;
    int mid=(l+r)>>1;
    if(x<=mid)Seg_change(now<<1,l,mid,x,Now,Pre);
    else Seg_change(now<<1|1,mid+1,r,x,Now,Pre);
}
void Seg_query(int now,int l,int r,int L,int R,int num)
{
    if(l==L&&r==R)
    {
        Tr.Find(Tr.root[now],num);
        return;
    }
    int mid = (l+r)>>1;
    if(mid>=R)
        Seg_query(now<<1,l,mid,L,R,num);
    else if(mid<L)
        Seg_query(now<<1|1,mid+1,r,L,R,num);
    else{
        Seg_query(now<<1,l,mid,L,mid,num);
        Seg_query(now<<1|1,mid+1,r,mid+1,R,num);
    }
}
///////////////////////////
int a[maxn];
int main()
{
    int T;scanf("%d",&T);
    while(T--)
    {
        Tr.init();
        int n,m;scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++){
            scanf("%d",&a[i]);
            Seg_insert(1,1,n,i,a[i]);
        }
        char op[3];
        for(int i=1;i<=m;i++){
            scanf("%s",op);
            if(op[0]=='C'){
                int x,y;scanf("%d%d",&x,&y);
                Seg_change(1,1,n,x,y,a[x]);
                a[x]=y;
            }
            else{
                int x,y,z;scanf("%d%d%d",&x,&y,&z);
                int l = 0,r = 1e9;
                while(l<=r){
                    int mid = (l+r)>>1;
                    tmp=0; Seg_query(1,1,n,x,y,mid);
                    if(tmp>=z)r=mid-1;
                    else l=mid+1;
                }
                printf("%d\n",l);
            }
        }
    }
    return 0; 
}

(2700ms左右);

 这题还有主席树(200ms左右)

代码见:http://www.cnblogs.com/hua-dong/p/7931778.html

posted @ 2017-11-29 16:24  nimphy  阅读(312)  评论(0编辑  收藏  举报