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);
		}
	}
}
posted @ 2022-01-10 16:00  Prean  阅读(26)  评论(0编辑  收藏  举报
var canShowAdsense=function(){return !!0};