LGP6011题解
昨天考试考到了这道题,那就来补一下题解吧。
题意简单不再阐述。
首先删除之后还要向左移动,很容易想到 ODT 平衡树,这个过于一眼,不再阐述。
重点说第二种方法。
向左平移的这个操作,我们是否可以用别的方法代替呢?
比如不向左平移,后面的操作时“修正”下标。
对于“修正”操作,可以二分。直接在线段树上二分/在树状数组上倍增(也就是在树状数组上二分)。
二分有一个要注意的细节是:如果二分的是最右边的满足条件的端点,记得不要 Find(x) 而是 Find(x-1)+1。
查询和修改操作就是线段树板子。
代码:
#include<cstdlib>
#include<cstdio>
#include<cctype>
const int M=1e6+5,INF=0x7fffffff;
int n,m,G,tmp,t[M],a[M];
struct Node{
int mx,mi;
Node(const int&mx=-INF,const int&mi=INF):mx(mx),mi(mi){}
}zkw[M<<2],it;
inline int min(const int&a,const int&b){
return a>b?b:a;
}
inline int max(const int&a,const int&b){
return a>b?a:b;
}
inline void Add(register int id){
for(;id<=n;id+=id&-id)++t[id];
}
inline int Find(const int&id){
int len,ans=0,num=0;
for(len=tmp;len;len>>=1){
if(ans+len<=id+num+t[ans+len])num+=t[ans+=len];
}
return ans;
}
inline Node merge(const Node&a,const Node&b){
return Node(max(a.mx,b.mx),min(a.mi,b.mi));
}
inline void update(const int&u){
zkw[u]=merge(zkw[u<<1],zkw[u<<1|1]);
}
inline void Build(){
register int i;
for(G=1;G<=n+1;G<<=1);
for(i=1;i<=n;++i)zkw[G+i]=Node(a[i],a[i]);
for(i=G-1;i;--i)update(i);
}
inline void Modify(int id){
Add(id);
for(zkw[id+=G]=Node(),id>>=1;id;id>>=1)update(id);
}
inline Node Query(int L,int R){
Node ans=Node();
for(L+=G-1,R+=G+1;L^R^1;L>>=1,R>>=1){
if(~L&1)ans=merge(ans,zkw[L^1]);
if(R&1)ans=merge(ans,zkw[R^1]);
}
return ans;
}
signed main(){
register int i,x,L,R,opt;
scanf("%d%d",&n,&m);tmp=n;
for(i=1;i<=n;++i)scanf("%d",a+i);
while(tmp^(tmp&-tmp))tmp^=tmp&-tmp;Build();
for(i=1;i<=m;++i){
scanf("%d",&opt);
if(opt==1){
scanf("%d",&x);
Modify(Find(x-1)+1);
}
else{
scanf("%d%d",&L,&R);
it=Query(Find(L-1)+1,Find(R-1)+1);
printf("%d %d\n",it.mi,it.mx);
}
}
}