【bzoj3196】二逼平衡树【树套树】【线段树】【平衡树】【呵呵】

……

我承认我写change函数时确实213了- -

a[pos]应当在最后被修改,可我却忘了……

第一次交上去的时候爆了数组,WA了……

然后把数组开大,交上去,MLE了……

现在我证明长度为n的序列,他的平衡树节点数组只要开到(log(2,f(n))+1)*f(n)就绝对不会爆。

f(n)是指比n大的最小的2的幂,比如f(65535)=65536,f(65537)=2^17.

证明嘛,因为长16的线段树有5层,每层平衡树总数都是16……

下面放代码,请神犇轻喷- -

欢迎评论。

#include<cstdio>
#include<cstring>
#include<iostream>
#define mid (l+r>>1)
using namespace std;
const int maxn=1200000;
typedef int arr[maxn];
int tot,m,n;
arr l,r,s,d;
int a[50001];
struct SBT{
	int rt;
	SBT():rt(0){}
	void L(int &x){
		int b=r[x];
		r[x]=l[b];
		l[b]=x;
		s[b]=s[x];
		s[x]=s[l[x]]+s[r[x]]+1;
		x=b;
	}
	void R(int &x){
		int b=l[x];
		l[x]=r[b];
		r[b]=x;
		s[b]=s[x];
		s[x]=s[l[x]]+s[r[x]]+1;
		x=b;
	}
	void maintain(int &x,bool f){
		if(!f){
			if(s[l[l[x]]]>s[r[x]])
				R(x);
			else if(s[r[l[x]]]>s[r[x]])
				L(l[x]),R(x);
			else return;
		}
		else{
			if(s[r[r[x]]]>s[l[x]])
				L(x);
			else if(s[l[r[x]]]>s[l[x]])
				R(r[x]),L(x);
			else return;
		}
		maintain(l[x],0);
		maintain(r[x],1);
		maintain(x,0);
		maintain(x,1);
	}
	void insert(int &x,int key){
		if(!x){
			x=++tot;
			s[x]=1;
			l[x]=r[x]=0;
			d[x]=key;
		}
		else{
			s[x]++;
			insert(key<d[x]?l[x]:r[x],key);
			maintain(x,key>=d[x]);
		}
	}
	int del(int &x,int key){
		s[x]--;
		if(key==d[x]||key<d[x]&&!l[x]||key>d[x]&&!r[x]){
			int val=d[x];
			if(!l[x]||!r[x])
				x=l[x]+r[x];
			else d[x]=del(l[x],key+1);
			return val;
		}
		return del(key<d[x]?l[x]:r[x],key);
	}
	int rank(int t,int key){
		int ans=0;//因为要把多个区间的结果并起来,所以只返回比key小的数的个数。 
		while(t){
			if(key<=d[t]) t=l[t];
			else ans+=s[l[t]]+1,t=r[t];
		}
		return ans;
	}
	int succ(int t,int key){
		int f=0;
		while(t){
			if(key<d[t]) f=t,t=l[t];
			else t=r[t];
		}
		return f?d[f]:0x1f1f1f1f;
	}
	int pred(int t,int key){
		int f=0;
		while(t){
			if(d[t]<key) f=t,t=r[t];
			else t=l[t];
		}
		return f?d[f]:-1;
	}
	void insert(int key){insert(rt,key);	 }
	void del   (int key){del(rt,key);	 }
	int  select(int k  ){return select(rt,k);}
	int  rank  (int k  ){return rank(rt,k);  }
	int  succ  (int key){return succ(rt,key);}
	int  pred  (int key){return pred(rt,key);}
};
//----------------------------------------------
//segment tree:
arr lc,rc;
SBT sbt[100000];
int stot=0,tree=0;
void build(int &x,int l,int r){
	x=++stot;
	for(int i=l;i<r;++i)//!!!!!!!!!!!!!!!!!!!!
		sbt[x].insert(a[i]);
	if(l+1<r){
		build(lc[x],l,mid);
		build(rc[x],mid,r);
	}
}
void change(int x,int l,int r,int pos,int c){
	if(l+1==r){
		sbt[x].del(a[pos]);
		sbt[x].insert(c);
		a[pos]=c;
		return;
	}
	sbt[x].del(a[pos]);
	sbt[x].insert(c);
	if(pos<mid)
		change(lc[x],l,mid,pos,c);
	else
		change(rc[x],mid,r,pos,c);

}
int rank(int x,int l,int r,int L,int R,int key){
	if(L<=l&&r<=R)
		return sbt[x].rank(key);
	int ans=0;
	if(L<mid) ans+=rank(lc[x],l,mid,L,R,key);
	if(R>mid) ans+=rank(rc[x],mid,r,L,R,key);
	return ans;
}
int succ(int x,int l,int r,int L,int R,int key){
	if(L<=l&&r<=R) return sbt[x].succ(key);
	int ans=0x1f1f1f1f;
	if(L<mid) ans=min(ans,succ(lc[x],l,mid,L,R,key));
	if(R>mid) ans=min(ans,succ(rc[x],mid,r,L,R,key));
	return ans;
}
int pred(int x,int l,int r,int L,int R,int key){
	if(L<=l&&r<=R) return sbt[x].pred(key);
	int ans=-1;
	if(L<mid) ans=max(ans,pred(lc[x],l,mid,L,R,key));
	if(R>mid) ans=max(ans,pred(rc[x],mid,r,L,R,key));
	return ans;
}
#undef mid
inline int Select(int L,int R,int k){
	int low=0,high=(int)1e8,tmpRank,mid;
	while(low<=high){
		mid=low+high>>1;
		tmpRank=1+rank(1,1,n+1,L,R,mid);
		if(tmpRank>k) high=mid-1;
		else low=mid+1;
	}
	return high;
}
inline int Succ(int L,int R,int key){
	return succ(1,1,n+1,L,R,key);
}
inline int Pred(int L,int R,int key){
	return pred(1,1,n+1,L,R,key);
}
inline int Rank(int L,int R,int key){
	return 1+rank(1,1,n+1,L,R,key);
}
inline void read(int &x){
    x=0;
    bool f=false;
    char ch=getchar();
    while((ch<'0'||ch>'9')&&ch!=-1){if(ch=='-') f=true;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-48;ch=getchar();}
    if(f) x=-x;
}
int main(){
	read(n),read(m);
	for(int i=1;i<=n;++i)
		read(a[i]);
	build(tree,1,n+1);
	int a,b,c,opt;
	while(m--){
		read(opt);
		switch(opt){
			case 1://求排名
				read(a);read(b);read(c);//scanf("%d%d%d",&a,&b,&c);
				printf("%d\n",Rank(a,b+1,c));
				break;
			case 2://求区间第k大
				read(a);read(b);read(c);//scanf("%d%d%d",&a,&b,&c);
				printf("%d\n",Select(a,b+1,c));
				break;
			case 3://修改元素的值
				read(a);read(b);//scanf("%d%d",&a,&b);
				change(1,1,n+1,a,b);
				break;
			case 4://求区间前驱
				read(a);read(b);read(c);//scanf("%d%d%d",&a,&b,&c);
				printf("%d\n",Pred(a,b+1,c));
				break;
			case 5://求区间后继
				read(a);read(b);read(c);//scanf("%d%d%d",&a,&b,&c);
				printf("%d\n",Succ(a,b+1,c));
				
		}
	}
	return 0;
}

PS:区间后继的求法(区间前驱同理):当这个区间被线段树分成好几个区间之后,在这几个区间里分别求后继,然后取最小值。注意如果在某个区间里没有后继,则返回正无穷,以免影响答案。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值