洛谷 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;
}
平衡树
先鸽着