[BZOJ1901][ZOJ2112]Dynamic Rankings-带修改的主席树-树状数组

Dynamic Rankings

Description

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


纪念咱有史以来第一次工业题(对于咱这种蒟蒻来说这就是工业题)一遍通过~

(ノ゚▽゚)ノ♪

另外,没权限的后果就是像咱一样去挑战时限和空间更加丧病的ZOJ版……
本来是有的啊谁把咱们的号权限取消了啊啊啊

(╯‵□′)╯︵┻━┻


题意:(咱觉得这英文有必要有题意这种东西)
多组询问。
给出一个序列,有两种操作:
1.询问区间第K大。
2.单点修改值。(刚看完题面的咱:求第K大的题还™有这种操作……)

思路:
显然咱这是第一次写这种奇奇怪怪的东西……
那就让咱口胡一下大致过程吧。

如果对于每个单点修改,咱去暴力地修改所有主席树,那么因为主席树维护前缀和,从被修改位置开始往后的所有树全都要被修改。
就算不说时间,修改一棵树空间复杂度O(logn),咱选择死亡……

有没有什么好一点的方法呢?
然后由于树状数组的前缀和特性,咱想到了它。

首先,咱先离线出所有询问,把值离散化以后很普通的像以前一样对初始序列建一排主席树。
注意,这里所有的主席树必须都是满的,也就是说从一开始就要建一棵完整的空树,而不是像原来一样可以一条链一条链的插入。
然后,咱再开一个新数组,作为咱的树状数组,只是每个节点保存的是一棵目前为止为空树的主席树的根节点(可以直接拷贝原来的空树)~

那么,对于每次修改,在树状数组中把往常在这个位置单点修改所影响的位置处的每一棵主席树上,都把原值删去,修改成这个新值,就像普通的主席树一样修改就好~
对于查询,开一个临时数组,事先把树状数组中根据往常将会参与前缀和计算位置的的所有根节点压进去,在原树上跑询问的同时对这些树同步跑询问(也就是同时递归向同一方向走),把所有树的值都凑在一起加一加减一减,像往常一样根据值决定递归到左儿子还是右儿子就好了~

很显然这样能压空间与时间的原因在于树状数组的前缀和特性,这不是很棒吗~
注意在这里咱对原序列建出的主席树始终没有变过,变过的只有树状数组中的每次几棵的树,这也是空间和时间上很优的原因。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>

using namespace std;

inline int read()
{
    int x=0;char ch=getchar();
    while(ch<'0' || '9'<ch)ch=getchar();
    while('0'<=ch && ch<='9')x=x*10+(ch^48),ch=getchar();
    return x;
}

inline int maxx(int a,int b){if(a>b)return a;return b;}
inline int minn(int a,int b){if(a<b)return a;return b;}

const int N=60009;
const int Q=10009;

struct operate
{
    int type,l,r,k;
}op[Q];

int n,m,q;
int rt[N],bit[N],a[N],ha[N],hat;
int t[N<<5],l[N<<5],r[N<<5],pool;
int usel[N],user[N];

inline int biu(int ll,int rr)
{
    int now=++pool;
    t[now]=0;
    if(ll<rr)
    {
        int mid=ll+rr>>1;
        l[now]=biu(ll,mid);
        r[now]=biu(mid+1,rr);
    }
    return now;
}

inline int update(int pre,int ll,int rr,int k,int cnt)
{
    int now=++pool;
    l[now]=l[pre];r[now]=r[pre];
    t[now]=t[pre]+cnt;

    if(ll<rr)
    {
        int mid=ll+rr>>1;
        if(k<=mid)
            l[now]=update(l[pre],ll,mid,k,cnt);
        else
            r[now]=update(r[pre],mid+1,rr,k,cnt);
    }

    return now;
}

inline int lowbit(int x)
{
    return x&(-x);
}

inline int query(int t1,int t2,int ll,int rr,int vl,int vr,int k)
{
    if(ll>=rr)
        return ll;

    int mid=ll+rr>>1;
    int val=t[l[t2]]-t[l[t1]];

    for(int i=vl;i;i-=lowbit(i))
        val-=t[l[usel[i]]];
    for(int i=vr;i;i-=lowbit(i))
        val+=t[l[user[i]]];

    if(val>=k)
    {
        for(int i=vl;i;i-=lowbit(i))
            usel[i]=l[usel[i]];
        for(int i=vr;i;i-=lowbit(i))
            user[i]=l[user[i]];
        return query(l[t1],l[t2],ll,mid,vl,vr,k);
    }
    else
    {
        for(int i=vl;i;i-=lowbit(i))
            usel[i]=r[usel[i]];
        for(int i=vr;i;i-=lowbit(i))
            user[i]=r[user[i]];
        return query(r[t1],r[t2],mid+1,rr,vl,vr,k-val);
    } 
}

inline void modify(int l,int k,int val)
{
    while(l<=n)
    {
        bit[l]=update(bit[l],1,m,k,val);
        l+=lowbit(l);
    }
}

int main()
{
    int T=read();
    while(T--)
    {
        n=read();q=read();
        pool=m=0;

        for(int i=1;i<=n;i++)
            ha[++m]=a[i]=read();

        char ch[4];
        for(int i=1;i<=q;i++)
        {
            scanf("%s",ch);
            if(ch[0]=='Q')
            {
                op[i].l=read();
                op[i].r=read();
                op[i].k=read();
                op[i].type=1;
            }
            else
            {
                op[i].l=read();
                op[i].k=read();
                ha[++m]=op[i].k;
                op[i].type=0;
            }
        }

        sort(ha+1,ha+m+1);
        m=unique(ha+1,ha+m+1)-ha-1;

        rt[0]=biu(1,m);
        for(int i=1;i<=n;i++)
            rt[i]=update(rt[i-1],1,m,lower_bound(ha+1,ha+m+1,a[i])-ha,1);
        for(int i=1;i<=n;i++)
            bit[i]=rt[0];

        for(int i=1;i<=q;i++)
        {
            if(op[i].type)
            {
                for(int j=op[i].l-1;j;j-=lowbit(j))
                    usel[j]=bit[j];
                for(int j=op[i].r;j;j-=lowbit(j))
                    user[j]=bit[j];
                printf("%d\n",ha[query(rt[op[i].l-1],rt[op[i].r],1,m,op[i].l-1,op[i].r,op[i].k)]);
            }
            else
            {
                modify(op[i].l,lower_bound(ha+1,ha+m+1,a[op[i].l])-ha,-1);
                modify(op[i].l,lower_bound(ha+1,ha+m+1,op[i].k)-ha,1);
                a[op[i].l]=op[i].k;
            }
        }
    }

    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值