洛谷 P2596 [ZJOI2006]书架

题目链接

本题目有两种做法:平衡树和树状数组

其实这两种做法有异曲同工之妙

树状数组

我们在原来的数组两端分别再接一段数组

变成:

\(_,_,_,_,_,_,_,_,_,_,1,3,2,7,5,8,10,4,9,6,_,_,_,_,_,_,_,_,_,_\)

我们维护两个信息 ,Pos数组和a数组

Pos[i]表示编号为i的书对应在上面的数组的下标

a[i]表示上面数组第i位书对应的编号

再维护一个树状数组

其前缀和S[i]表示上面数组前i位上共有几本书

Top操作

例:Top 7

\(_,_,_,_,_,_,_,_,_,_,1,3,2,7,5,8,10,4,9,6,_,_,_,_,_,_,_,_,_,_\)

\(_,_,_,_,_,_,_,_,_,7,1,3,2,_,5,8,10,4,9,6,_,_,_,_,_,_,_,_,_,_\)

Bottom操作

例:Bottom 4

\(_,_,_,_,_,_,_,_,_,_,1,3,2,7,5,8,10,4,9,6,_,_,_,_,_,_,_,_,_,_\)

\(_,_,_,_,_,_,_,_,_,_,1,3,2,7,5,8,10,_,9,6,4,_,_,_,_,_,_,_,_,_\)

以上两种操作本质一样

我们用两个变量l,r来记录书的所在位置的范围,每次操作就是将原来的书本取出,放在左端点左边或右端点右边

然后--l或++r

Ask操作

答案即为query(Pos[s])-1

query为树状数组查询函数

Query操作

有点麻烦,我们采用二分套查询的方法 \(O(log^2n)\)解决问题

首先S数组为前缀和数组,其值具有单调性,若S[i]=s&&S[i-1]=s-1,则i即为第s本书所在位置,答案即为a[i]

在l,r的范围内二分,每次用query(i)获取S[i]

最后就能得到答案

Insert操作

相当于是交换前后两本书,我们考虑直接找到另一本书,swap二者的对应数组的值即可

Insert s t

t=0直接continue

否则先找到编号为s的书是第几本 sNum 等同于Ask操作

然后得到另一本书是第几本tNum=sNum+t

然后得到另一本书的位置 tPos 等同于Query操作

最后得到两本书的所有信息

编号s||位置sPos=Pos[s]||第几本sNum

编号tId=a[tPos]||位置tPos||第几本tNum

更新(swap) a[sPos]和a[tPos],Pos[s]和Pos[tId]

小心一个swap后,其值就变了,不能用来定位另一个值,第二个swap会出错

[Code]


#include <bits/stdc++.h>
using namespace std;

int read(){
    int x=0,flag=1; char c;
    for(c=getchar();!isdigit(c);c=getchar()) if(c=='-') flag=-1;
    for(;isdigit(c);c=getchar()) x=((x+(x<<2))<<1)+(c^48);
    return x*flag;
}

const int N=250000;
const int gap=80005;
int n,m;

int c[N];
int lowbit(int x) { return x&(-x); }
int query(int x){
    int ret=0;
    for(int i=x;i>0;i-=lowbit(i)) ret+=c[i];
    return ret;
}
void update(int x,int y){
    for(int i=x;i<=250000;i+=lowbit(i)) c[i]+=y;
}

int pos[N],a[N];
int l,r;

int main() {
    n=read(),m=read();
    
    for(int i=1;i<=n;i++){
        int x=read();
        pos[x]=i+gap;
        a[i+gap]=x; 
        update(i+gap,1);
    }
    
    l=gap+1; r=gap+n;
    while(m--){
        
        char s[15];
        int x,y;
        scanf(" %s %d",s,&x);
        
        if(s[0]=='T'){
            update(pos[x],-1);
            a[pos[x]]=0;
            
            pos[x]=--l; update(pos[x],1);
            a[pos[x]]=x;
        }
        
        if(s[0]=='B'){
            update(pos[x],-1);
            a[pos[x]]=0;
            
            pos[x]=++r; update(pos[x],1);
            a[pos[x]]=x;
        }
        
        if(s[0]=='I'){
            int y=read();
            if(y==0) continue;
            
            int z=query(pos[x])+y;
            
            int ll=l,rr=r,ans=-1;
            while(ll<=rr){
                int mid=(ll+rr)>>1;
                if(query(mid)>=z) { rr=mid-1; ans=mid; }
                else ll=mid+1;
            }
            
            int p=pos[x];
            swap(pos[a[ans]],pos[x]); 
            swap(a[p],a[ans]);
        }
        
        if(s[0]=='A'){
            printf("%d\n",query(pos[x])-1);
        }
        if(s[0]=='Q'){
            int ll=l,rr=r,ans=-1;
            while(ll<=rr){
                int mid=(ll+rr)>>1;
                if(query(mid)>=x) { rr=mid-1; ans=mid; }
                else ll=mid+1;
            }
            printf("%d\n",a[ans]);
        }
    }
    return 0;
}

平衡树

先鸽着

猜你喜欢

转载自www.cnblogs.com/zzhzzh123/p/12228211.html