[BZOJ 3196] 二逼平衡树

Link:

BZOJ 3196 传送门

Solution:

最直观的的思路是用线段树套平衡树

不过一看到区间第$k$大就又忍不住去写最近刚练的带修改主席树了

感觉自己数据结构题灵活变通的能力还不够强啊,一开始$naive$得觉得不好算排名……

 

区间第$k$大和修改的操作和$Dynamic Rankings$那道经典题完全相同

对于排名只要利用每个节点的$cnt$计算小于等于$k-1$的个数就能算出$k$的排名了

而后面两个询问完全可以用前两个操作求出:

先算出前驱/后继的排名$t$,再求第$t$大的值就行了

 

Tip:注意离散化时要包含所有询问中出现的值,否则$lower\_ bound$时会出锅……

Code:

#include <bits/stdc++.h>

using namespace std;
const int MAXN=5e4+10;
struct Query{int l,r,k,op;}qry[MAXN];
struct PrTree{int ls,rs,cnt;}seg[MAXN*100];
int n,m,dat[MAXN],dsp[MAXN*6],rt[MAXN],L[MAXN],R[MAXN],cntl,cntr,tot,cnt;

int lowbit(int x){return x&(-x);}
int idx(int x)
{return lower_bound(dsp+1,dsp+tot+1,x)-dsp;}
void Update(int &cur,int pos,int val,int l,int r)
{
    if(!cur) cur=++cnt;
    seg[cur].cnt+=val;
    if(l==r) return;int mid=(l+r)>>1;
    if(pos<=mid) Update(seg[cur].ls,pos,val,l,mid);
    else Update(seg[cur].rs,pos,val,mid+1,r);
}
int Query(int cur,int pos,int l,int r)
{
    if(l==r) return seg[cur].cnt;
    int mid=(l+r)>>1;
    if(pos<=mid) return Query(seg[cur].ls,pos,l,mid);
    else return Query(seg[cur].rs,pos,mid+1,r)+seg[seg[cur].ls].cnt;
}
int Kth(int k,int l,int r)
{
    if(l==r) return l;
    int mid=(l+r)>>1,sum=0;
    for(int i=1;i<=cntl;i++) sum-=seg[seg[L[i]].ls].cnt;
    for(int i=1;i<=cntr;i++) sum+=seg[seg[R[i]].ls].cnt;
    
    if(k<=sum)
    {
        for(int i=1;i<=cntl;i++) L[i]=seg[L[i]].ls;
        for(int i=1;i<=cntr;i++) R[i]=seg[R[i]].ls;
        return Kth(k,l,mid);
    }
    else
    {
        for(int i=1;i<=cntl;i++) L[i]=seg[L[i]].rs;
        for(int i=1;i<=cntr;i++) R[i]=seg[R[i]].rs;
        return Kth(k-sum,mid+1,r);
    }
}

void upd(int pos,int val)
{
    for(int i=pos;i<=n;i+=lowbit(i))
        Update(rt[i],dat[pos],val,1,tot);
}
int find_rank(int l,int r,int k)
{
    int sum=0;
    for(int i=l-1;i;i-=lowbit(i))
        sum-=Query(rt[i],k,1,tot);
    for(int i=r;i;i-=lowbit(i))
        sum+=Query(rt[i],k,1,tot);
    return sum;
}
int find_kth(int l,int r,int k)
{
    cntl=cntr=0;
    for(int i=l-1;i;i-=lowbit(i)) L[++cntl]=rt[i];
    for(int i=r;i;i-=lowbit(i)) R[++cntr]=rt[i];
    return dsp[Kth(k,1,tot)];
}

int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
        scanf("%d",&dat[i]),dsp[++tot]=dat[i];
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d%d",&qry[i].op,&qry[i].l,&qry[i].r);
        if(qry[i].op==3) dsp[++tot]=qry[i].r;
        else
        {//一定要将全部数据录入离散化数组! 
            scanf("%d",&qry[i].k);
            if(qry[i].op!=2) dsp[++tot]=qry[i].k;
        }
    }
    sort(dsp+1,dsp+tot+1);tot=unique(dsp+1,dsp+tot+1)-dsp-1;
    
    for(int i=1;i<=n;i++)
        dat[i]=idx(dat[i]),upd(i,1);
    
    for(int i=1;i<=m;i++)
    {
        int l=qry[i].l,r=qry[i].r,k=qry[i].k;
        switch(qry[i].op)
        {
            case 1:printf("%d\n",find_rank(l,r,idx(k)-1)+1);break;
            case 2:printf("%d\n",find_kth(l,r,k));break;
            case 3:upd(l,-1);dat[l]=idx(r);upd(l,1);break;
            case 4:
            {
                int t=find_rank(l,r,idx(k)-1);
                if(!t){puts("-2147483647");break;}
                printf("%d\n",find_kth(l,r,t));break;
            }
            case 5:
            {
                int t=find_rank(l,r,idx(k));
                if(t==r-l+1){puts("2147483647");break;}
                printf("%d\n",find_kth(l,r,t+1));break;
            }
        }
    }
    
    return 0;
}

 

posted @ 2018-07-25 22:36  NewErA  阅读(155)  评论(0编辑  收藏  举报