BZOJ[1901]Zju2112 Dynamic Rankings 树套树(树状数组+线段树)

21 篇文章 0 订阅
10 篇文章 0 订阅

传送门ber~

对于不修改的区间 k k 大,可以用主席树搞前缀和,但不支持修改
可以把前缀和用树状数组维护,logn修改, logn log n 查询
就是把 n n <script type="math/tex" id="MathJax-Element-20">n</script>个线段树像树状数组那样串起来
具体还是看代码吧..

线段树套Splay版本看这里

代码如下:

#include<algorithm>
#include<ctype.h>
#include<cstdio>
#define N 10020
using namespace std;
inline int read(){
    int x=0,f=1;char c;
    do c=getchar(),f=c=='-'?-1:f; while(!isdigit(c));
    do x=(x<<3)+(x<<1)+c-'0',c=getchar(); while(isdigit(c));
    return x*f;
}
int n,m,topL,topR,tot;
int a[N],b[30020];
char c[5];
struct Seg{
    int sum;
    Seg *ls,*rs;
    Seg():ls(NULL),rs(NULL),sum(0){}
    inline void maintain(){
        sum=0;
        if(ls) sum+=ls->sum;
        if(rs) sum+=rs->sum;
    }
}*root[N],*L[N],*R[N];
struct Question{
    int x,y,k;
}q[N];
void Add(int L,int R,int x,int v,Seg *&k){
    if(!k) k=new Seg;
    if(L==R){
        k->sum+=v;
        return;
    }
    int mid=L+R>>1;
    if(x<=mid) Add(L,mid,x,v,k->ls);
    else Add(mid+1,R,x,v,k->rs);
    k->maintain();
    return;
}
int Query(int l,int r,int k){
    if(l==r) return l;
    int sumL,sumR;
    sumL=sumR=0;
    for(int i=1;i<=topL;i++)
        if(L[i] && L[i]->ls)
            sumL+=L[i]->ls->sum;
    for(int i=1;i<=topR;i++)
        if(R[i] && R[i]->ls)
            sumR+=R[i]->ls->sum;
    int mid=l+r>>1;
    if(sumR-sumL>=k){
        for(int i=1;i<=topL;i++)
            if(L[i]) L[i]=L[i]->ls;
        for(int i=1;i<=topR;i++)
            if(R[i]) R[i]=R[i]->ls;
        return Query(l,mid,k);
    }
    else{
        for(int i=1;i<=topL;i++)
            if(L[i]) L[i]=L[i]->rs;
        for(int i=1;i<=topR;i++)
            if(R[i]) R[i]=R[i]->rs;
        return Query(mid+1,r,k-(sumR-sumL));
    }
}
int main(){
    ///读入
    n=read();m=read();
    for(int i=1;i<=n;i++)
        b[++tot]=a[i]=read();
    for(int i=1;i<=m;i++){
        scanf("%s",c+1);
        q[i].x=read();q[i].y=read();
        if(c[1]=='C') q[i].k=0,b[++tot]=q[i].y;
        else q[i].k=read();
    }
    ///离散化
    sort(b+1,b+tot+1);
    tot=unique(b+1,b+tot+1)-b-1;
    for(int i=1;i<=n;i++) a[i]=lower_bound(b+1,b+tot+1,a[i])-b;
    for(int i=1;i<=m;i++) if(!q[i].k) q[i].y=lower_bound(b+1,b+tot+1,q[i].y)-b;
    ///建树
    for(int i=1;i<=n;i++)
        for(int j=i;j<=n;j+=j&(-j))
            Add(1,tot,a[i],1,root[j]);
    ///solve
    for(int i=1;i<=m;i++){
        if(!q[i].k){
            for(int j=q[i].x;j<=n;j+=j&(-j))
                Add(1,tot,a[q[i].x],-1,root[j]);
            a[q[i].x]=q[i].y;
            for(int j=q[i].x;j<=n;j+=j&(-j))
                Add(1,tot,a[q[i].x],1,root[j]);
        }
        else{
            topL=topR=0;
            for(int j=q[i].x-1;j;j-=j&(-j))
                L[++topL]=root[j];///存下左端点是由哪几个线段树拼出来的
            for(int j=q[i].y;j;j-=j&(-j))
                R[++topR]=root[j];
            printf("%d\n",b[Query(1,tot,q[i].k)]);
        }
    }
return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值