bzoj 1901: Zju2112 Dynamic Rankings -- 主席树,树状数组,哈希

1901: Zju2112 Dynamic Rankings

Time Limit: 10 Sec  Memory Limit: 128 MB

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继续回答上面的问题。

Input

第一行有两个正整数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

m,n≤10000

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

如果查询第k大,我们直接用主席树就可以了,但是加上了修改操作,就需要再加上一维树状数组维护

这样就在每个lowbit修改就能维护出所有信息,查询同理。时间复杂度O(nlog2n)

#include<map>
#include<cmath>
#include<queue>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define inf 1000000007
#define ll long long
#define M 10000010
#define N 10010
inline int rd()
{
    int x=0,f=1;char ch=getchar();
    while(ch<‘0‘||ch>‘9‘){if(ch==‘-‘)f=-1;ch=getchar();}
    while(ch>=‘0‘&&ch<=‘9‘){x=x*10+ch-‘0‘;ch=getchar();}
    return x*f;
}
int n,m,v[N],A[N],B[N],K[N];
int hs[N<<1],ji[N<<1],tt,tot=1;
int rt[N],sz[M],ls[M],rs[M],cnt;
char s[10];
bool q[N];
int hsh(int x)
{
    int l=1,r=tot,mid,mz;
    while(l<=r)
    {
        mid=l+r>>1;
        if(hs[mid]<x) l=mid+1;
        else r=mid-1,mz=mid;
    }
    return mz;
}
void add(int lst,int l,int r,int &p,int x,int w)
{
    p=++cnt;
    sz[p]=sz[lst]+w;
    ls[p]=ls[lst];rs[p]=rs[lst];
    if(l==r) return;
    int mid=l+r>>1;
    if(x<=mid) add(ls[lst],l,mid,ls[p],x,w);
    else add(rs[lst],mid+1,r,rs[p],x,w);
}
int a,b,L[33],R[33];
int fd(int l,int r,int k)
{
    if(l==r) return l;
    int mid=l+r>>1,sl=0,sr=0;
    for(int i=1;i<=a;i++) sl+=sz[ls[L[i]]];
    for(int i=1;i<=b;i++) sr+=sz[ls[R[i]]];
    if(sr-sl>=k)
    {
        for(int i=1;i<=a;i++) L[i]=ls[L[i]];
        for(int i=1;i<=b;i++) R[i]=ls[R[i]];
        return fd(l,mid,k);
    }
    else
    {
        for(int i=1;i<=a;i++) L[i]=rs[L[i]];
        for(int i=1;i<=b;i++) R[i]=rs[R[i]];
        return fd(mid+1,r,k-sr+sl);
    }
}
int main()
{
    tt=n=rd();m=rd();
    for(int i=1;i<=n;i++) ji[i]=v[i]=rd();
    for(int i=1;i<=m;i++)
    {
        scanf("%s",s);A[i]=rd();B[i]=rd();
        if(s[0]==‘Q‘) K[i]=rd();
        else ji[++tt]=B[i],q[i]=1;
    }
    sort(ji+1,ji+tt+1);
    hs[1]=ji[1];
    for(int i=2;i<=tt;i++)
        if(ji[i]!=ji[i-1]) hs[++tot]=ji[i];
    for(int i=1,tp;i<=n;i++)
    {
        tp=hsh(v[i]);
        for(int j=i;j<=n;j+=j&(-j))
            add(rt[j],1,tot,rt[j],tp,1);
    }
    for(int i=1,tp;i<=m;i++)
    {
        if(q[i])
        {
            tp=hsh(v[A[i]]);
            for(int j=A[i];j<=n;j+=j&(-j))
                add(rt[j],1,tot,rt[j],tp,-1);
            tp=hsh(B[i]);
            for(int j=A[i];j<=n;j+=j&(-j))
                add(rt[j],1,tot,rt[j],tp,1);
            v[A[i]]=B[i];
        }
        else
        {
            a=b=0;
            for(int j=A[i]-1;j;j-=j&(-j)) L[++a]=rt[j];
            for(int j=B[i];j;j-=j&(-j)) R[++b]=rt[j];
            printf("%d\n",hs[fd(1,tot,K[i])]);
        }
    }
    return 0;
}
时间: 2024-11-10 13:52:39

bzoj 1901: Zju2112 Dynamic Rankings -- 主席树,树状数组,哈希的相关文章

BZOJ 1901: Zju2112 Dynamic Rankings 区间k大 带修改 在线 线段树套平衡树

之前写线段树套splay数组版..写了6.2k..然后弃疗了.现在发现还是很水的..嘎嘎.. zju过不了,超时. upd:才发现zju是多组数据..TLE一版才发现.然后改了,MLE...手写内存池..尼玛终于过了..附zju2112代码于后. bzoj倒是过了,1A的感觉还是很爽的..可是时间不好看..这就是所谓\(O(nlog^3n)\)的复杂度的可怜之处么? 写挂的地方: insert一定要是传地址指针进去. delete时先把地址指针delete掉,最后把是地址指针指向左儿子or右儿子

BZOJ 1901: Zju2112 Dynamic Rankings( 树状数组套主席树 )

裸的带修改主席树.. 之前用BIT套Splay( http://www.cnblogs.com/JSZX11556/p/4625552.html )A过..但是还是线段树好写...而且快(常数比平衡树小). 时空复杂度是O(Nlog(N)+Mlog^2(N)) ------------------------------------------------------------------------- #include<cstdio> #include<cstring> #in

bzoj 1901: Zju2112 Dynamic Rankings(树套树)

1901: Zju2112 Dynamic Rankings 经典的带修改求区间第k小值问题 树套树模板,我是用的线段树套splay实现的,而且用的数组模拟的,所以可能空间略大,bzoj过了,zoj过不了. 思路很简单,用线段树维护区间,用splay维护区间内的权值,然后询问的时候,二分答案key,然后在区间内找小于key的数有多少个. 贴上模板: #include<stdio.h> #include<string.h> #include<algorithm> #def

Bzoj 1901: Zju2112 Dynamic Rankings 树套树,线段树,平衡树,Treap

1901: Zju2112 Dynamic Rankings Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 6471  Solved: 2697[Submit][Status][Discuss] 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

bzoj 1901: Zju2112 Dynamic Rankings

Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 6245  Solved: 2593[Submit][Status][Discuss] 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继续回答上面的问题.你需

BZOJ 1901 Zju2112 Dynamic Rankings 题解

题意:带修改不带插入的区间k大. 裸的可持久化线段树..由于有修改,要用树状数组维护.其它跟不带修改的可持久化线段树一样. 因为我没有找到网上用指针写的代码..CLJ写这道题也用的不是可持久化线段树,于是我就没有任何模板可以参照..就参考网上数组版的自己脑补了一个指针版..你们就可以看到代码优美度下降了好多. 由于数字范围很大,我们需要把所有输入读进来然后离散化..不离散化的话就要动态开点(这个我暂时还不会) 这份代码用的空间比较多..在zju是A不了的,需要空间优化(怎么优化我也不知道) 1

1901: Zju2112 Dynamic Rankings

1901: Zju2112 Dynamic Rankings Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 5268  Solved: 2207[Submit][Status][Discuss] 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

【BZOJ 1901】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,然后读入一系列的指令,包括询问指令和修改指令.对于每一个询问指令,你必须输出正确的回答. (带修改的区间第K小) Input 第一行有两个正

zoj 2112 Dynamic Rankings(主席树&amp;动态第k大)

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