洛谷 P2617 Dynamic Rankings (带修改主席树)

题目链接:https://www.luogu.org/problem/P2617

思路:每个树状数组的点i的都是一颗长度为lowbit(i)的主席树,为原数组【i-lowbit(i)+1,i】的信息。

/*
洛谷 P2617
两个正整数n,m分别表示序列的长度和指令的个数。
第二行有n个数,表示a[1],a[2]……a[n]
接下来的m行描述每条指令,Q i j k 或者 C i t
Q i j k 表示询问指令,询问a[i],a[i+1]……a[j]中第k小的数。
C i t 表示把a[i]改变成为t。
5 3
3 2 1 4 7
Q 1 4 3
C 2 6
Q 2 5 3
ans:
3
6
*/
#include<bits/stdc++.h>
#define M 200005
using namespace std;
int jishu=0;
int t,n,m,len_disc,tot,cl,cr;
int A[M],disc[M],L[M],R[M],K[M],root[M],LL[M],RR[M];
char S[M][2];
struct node
{
    int l,r,sum;
    node() {}
    node(int _l,int _r,int _sum):l(_l),r(_r),sum(_sum){}
    //这个地方如果改成
    //node(int _l=0,int _r=0,int _sum=0):l(_l),r(_r),sum(_sum){}
    //会炸内存.....改成这样内存直接加从150M左右跳到大于1000M......
    //nmd老子找了一个下午的爆内存,改了这个就过了...艹
} T[600*M];
/*********主席树********/
void update(int &rt,int pos,int flag,int l,int r)//不用保存历史版本,所以可以直接修改
{
    jishu++;
    if(!rt) rt=++tot;
    T[rt].sum+=flag;
    if(l==r) return ;
    int mid=(l+r)>>1;
    if(pos<=mid) update(T[rt].l,pos,flag,l,mid);
    else update(T[rt].r,pos,flag,mid+1,r);
}
int query(int k,int l,int r)
{
    jishu++;
    if(l==r) return l;
    int d=0,mid=(l+r)>>1;
    for(int i=1; i<=cr; i++) d+=T[T[RR[i]].l].sum;
    for(int i=1; i<=cl; i++) d-=T[T[LL[i]].l].sum;
    if(k<=d)
    {
        for(int i=1; i<=cr; i++) RR[i]=T[RR[i]].l;
        for(int i=1; i<=cl; i++) LL[i]=T[LL[i]].l;
        return query(k,l,mid);
    }
    else
    {
        for(int i=1; i<=cr; i++) RR[i]=T[RR[i]].r;
        for(int i=1; i<=cl; i++) LL[i]=T[LL[i]].r;
        return query(k-d,mid+1,r);
    }
}
/*********树状数组********/
int lowbit(int x)
{
    return x&(-x);
}
int add(int i,int pos,int flag)//每一个点都是一颗长为lowbit(pos)的主席树
{
    jishu++;
    while(i<=n)
    {
        update(root[i],pos,flag,1,len_disc);
        i+=lowbit(i);
    }
}
/*********离散化********/
void init_discate()
{
    sort(disc+1,disc+1+len_disc);
    len_disc=unique(disc+1,disc+1+len_disc)-disc-1;
}
int discate(int x)
{
    return lower_bound(disc+1,disc+1+len_disc,x)-disc;
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1; i<=n; i++)
        scanf("%d",&A[i]),disc[++len_disc]=A[i];
    for(int i=1; i<=m; i++)
    {
        scanf("%s",S[i]);
        if(S[i][0]=='Q')
            scanf("%d%d%d",&L[i],&R[i],&K[i]);
        else
            scanf("%d%d",&L[i],&R[i]),disc[++len_disc]=R[i];//把后边要修改的值也放去离散化
    }
    init_discate();
    for(int i=1; i<=n; i++) add(i,discate(A[i]),1);
    for(int i=1; i<=m; i++)
    {
        if(S[i][0]=='Q')
        {
            cl=0,cr=0;
            for(int j=R[i]; j>0; j-=lowbit(j)) RR[++cr]=root[j];
            for(int j=L[i]-1; j>0; j-=lowbit(j)) LL[++cl]=root[j];//记录树状数组对应多颗线段树的根
            printf("%d\n",disc[query(K[i],1,len_disc)]);
        }
        else
        {
            add(L[i],discate(A[L[i]]),-1);
            A[L[i]]=R[i];
            add(L[i],discate(R[i]),1);
        }
    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值