1. 程式人生 > >二逼平衡樹-分塊

二逼平衡樹-分塊

題目地址【IN-luogubzoj,loj也有,但是題目可能有點不同,這裡的程式碼是luogu上的AC程式碼

本來想練練樹套樹,結果一看資料範圍,就試著寫了個大力分塊,結果過了-_-||,後面再練樹套樹吧。


對於這幾個操作,用分塊的話,暴力非常好寫:

我們先將原數列分塊,對於新的分了塊的數列每個快內排個序,複雜度 O ( n l

o g n ) O(nlogn)

  • 查詢k在區間內的排名

邊角暴力統計,塊內二分即可。

  • 查詢區間內排名為k的值

二分值,然後去統計這個值的排名即可

  • 修改某一位值上的數值

在原陣列上把它改了,然後將它所在的塊還原重新排個序。

  • 查詢k在區間內的前驅(前驅定義為嚴格小於x,且最大的數,若不存在輸出-2147483647)

邊角暴力,塊內二分

  • 查詢k在區間內的後繼(後繼定義為嚴格大於x,且最小的數,若不存在輸出2147483647)

同理

注意:這裡邊角暴力時,必須在原陣列上查詢,不能在排好序的上面找,否則會出錯。

複雜度為 n n l

o g n n\sqrt{n}log\sqrt{n} 卡的過去。


醜陋程式碼新鮮出爐:

這裡我分的300大小的塊。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int inf=0x7fffffff;
const int N=245,M=1e5+1,L=1e5+10;
int n,m,val[L],bef[L],up;
int stpos[N],enpos[N],bel[L],tot;
int calc_1(int l,int r,int v){
	int st=l,en=r;
	if(val[l]>v)return 0;
	int mid,ans=r+5;
	while(l<=r){
		mid=l+r>>1;
		if(val[mid]<v)l=mid+1,ans=mid;
		else r=mid-1;
	}
	if(ans>en||ans<st)return 0;
	return ans-st+1;
}
int find_rank(int l,int r,int k){
	int ans=0;
	if(bel[l]==bel[r]){
		for(int i=l;i<=r;i++){
			if(bef[i]<k)++ans;
		}
		return ans+1;
	}
	for(int i=l;i<=enpos[bel[l]];i++){if(bef[i]<k)++ans;}
	for(int i=stpos[bel[r]];i<=r;i++){if(bef[i]<k)++ans;}
	for(int i=bel[l]+1;i<bel[r];i++){ans+=calc_1(stpos[i],enpos[i],k);}	
	return ans+1;
}
int solve_1(){
	int l,r,k;
	scanf("%d%d%d",&l,&r,&k);
	return find_rank(l,r,k);
}
int solve_2(){
	int L=0,R=1e8,ans=R,mid,k,l,r,now;
	scanf("%d%d%d",&l,&r,&k);
	while(L<=R){
		mid=L+R>>1;
		now=find_rank(l,r,mid);
		if(now>k)R=mid-1;
		else L=mid+1,ans=mid;
	}
	return ans;
}
void solve_3(){
	int p,k;
	scanf("%d%d",&p,&k);
	bef[p]=k;
	for(int i=stpos[bel[p]];i<=enpos[bel[p]];i++)val[i]=bef[i];
	sort(val+stpos[bel[p]],val+enpos[bel[p]]+1);
}
int find_mid_4(int l,int r,int v){
	int mid,ans=-inf;
	while(l<=r){
		mid=l+r>>1;
		if(val[mid]<v){
			if(val[mid]>ans)ans=val[mid];
			l=mid+1;
		}else r=mid-1;
	}
	if(val[l]<v&&val[l]>ans)ans=val[l];
	return ans;
}
int find_mid_5(int l,int r,int v){
	int mid,ans=inf;
	while(l<=r){
		mid=l+r>>1;
		if(val[mid]>v){
			if(val[mid]<ans)ans=val[mid];
			r=mid-1;
		}else l=mid+1;
	}
	if(val[l]>v&&val[l]<ans)ans=val[l];
	return ans;
}
int solve_4(){
	int l,r,k,ans=-inf;
	scanf("%d%d%d",&l,&r,&k);
	if(bel[l]==bel[r]){
		for(int i=l;i<=r;i++){
			if(bef[i]<k){
				if(bef[i]>ans)ans=bef[i];
			}			
		}
		return ans;
	}	
	for(int i=l;i<=enpos[bel[l]];i++){
		if(bef[i]<k){
			if(bef[i]>ans)ans=bef[i];
		}
	}
	for(int i=stpos[bel[r]];i<=r;i++){
		if(bef[i]<k){
			if(bef[i]>ans)ans=bef[i];
		}
	}
	for(int i=bel[l]+1;i<bel[r];i++){
		int now=find_mid_4(stpos[i],enpos[i],k);
		if(now<k){
			if(now>ans)ans=now;
		}
	}
	return ans;
}
int solve_5(){
	int l,r,k,ans=inf;
	scanf("%d%d%d",&l,&r,&k);
	if(bel[l]==bel[r]){
		for(int i=l;i<=r;i++){
			if(bef[i]>k){
				if(bef[i]<ans)ans=bef[i];
			}			
		}
		return ans;
	}
	for(int i=l;i<=enpos[bel[l]];i++){
		if(bef[i]>k){
			if(bef[i]<ans)ans=bef[i];
		}
	}
	for(int i=stpos[bel[r]];i<=r;i++){
		if(bef[i]>k){
			if(bef[i]<ans)ans=bef[i];
		}
	}
	for(int i=bel[l]+1;i<bel[r];i++){
		int now=find_mid_5(stpos[i],enpos[i],k);
		if(now>k){
			if(now<ans)ans=now;
		}
	}
	return ans;	
}
int opt;
int main(){
	scanf("%d%d",&n,&m);
	int fj=0;up=n+m;
	for(int i=1;i<=n;i++){
		scanf("%d",&val[i]);bef[i]=val[i];
		if(!fj){
			fj=300;
			enpos[tot]=i-1;
			stpos[++tot]=i;
		}
		--fj;bel[i]=tot;
	}	enpos[tot]=n;
	for(int i=1;i<=tot;i++){sort(val+stpos[i],val+enpos[i]+1);}
	while(m--){
		scanf("%d",&opt);
		switch(opt){
			case 1:{
				printf("%d\n",solve_1());
				break;
			}
			case 2:{
				printf("%d\n",solve_2());
				break;
			}
			case 3:{
				solve_3();
				break;
			}
			case 4:{
				printf("%d\n",solve_4());
				break;
			}
			case 5:{
				printf("%d\n",solve_5());
				break;
			}
		}
	}
	return 0;
}