题目

Solution

把普通平衡树代码中 r t rt rt改成 r t [ p o s ] rt[pos] rt[pos],然后写一个线段树即可
说说很简单,但是写起来真的麻烦死,后来我是找了一份和我写得有点像的代码改了好久才改出来的,其中一个原因竟然是我原本的平衡树板子就是错的!!!’
复杂度 O ( n l o g 3 n ) O(nlog^3n) O(nlog3n)

Code

代码有点慢, b z o j bzoj bzoj上跑了 9916 m s 9916ms 9916ms
注释掉的代码旁边若没有文字,则说明是另一种写法,也是对的,只是慢一点,可以直接替换

#include<bits/stdc++.h>
using namespace std;
const int N=2000002,inf=2147483647;
#define lc ch[0]
#define rc ch[1]
int sz[N],fa[N],cnt[N],a[N],ans,mid,l,r,ll,rr,k,rt[N],tot,val[N],ch[2][N],n,m,i,opt;
inline char gc(){
	static char buf[100000],*p1=buf,*p2=buf;
	return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline int rd(){
	int x=0,fl=1;char ch=gc();
	for (;ch<48||ch>57;ch=gc())if(ch=='-')fl=-1;
	for (;48<=ch&&ch<=57;ch=gc())x=(x<<3)+(x<<1)+(ch^48);
	return x*fl;
}
inline void wri(int a){if(a<0)a=-a,putchar('-');if(a>=10)wri(a/10);putchar(a%10|48);}
inline void wln(int a){wri(a);puts("");}
//---------------------------------------------------------平衡树 
void upd(int x){sz[x]=sz[lc[x]]+sz[rc[x]]+cnt[x];}
void rot(int x){
	int y=fa[x],z=fa[y];bool f=x==rc[y];
	if (z) ch[y==rc[z]][z]=x;
	fa[x]=z,fa[y]=x,fa[ch[!f][x]]=y,ch[f][y]=ch[!f][x],ch[!f][x]=y;
	upd(y);
}
void splay(int pos,int x){
	while (fa[x]){
		int y=fa[x],z=fa[y];
		if (z) rot((y==rc[z])^(x==rc[y])?x:y);
		rot(x);
	}
	upd(x);
	rt[pos]=x;
}
void ins(int pos,int x){
	int t=rt[pos],ff=0;
	while (t){
		sz[t]++;
		if (x==val[t]){
			cnt[t]++;
			return;
		}
		ff=t,t=ch[x>val[t]][t];
	}
	t=++tot;
	ch[x>val[ff]][ff]=t;
	fa[t]=ff,val[t]=x,lc[t]=rc[t]=0,sz[t]=cnt[t]=1;
	splay(pos,t);
}
int find(int pos,int x){
	int t=rt[pos];
	do{
		if (x==val[t]) break;
		t=ch[x>val[t]][t];
	}while (1);
	splay(pos,t);
	return t;
}
int rank(int pos,int x){
	int t=rt[pos],ret=0;
	while (t){
		if (x==val[t]) return ret+sz[lc[t]];
		if (x>val[t]){
			ret+=sz[lc[t]]+cnt[t];
			t=rc[t];
		}else t=lc[t];
	}
	return ret;
}
//int Max(int x){return rc[x]?Max(rc[x]):x;}
int Max(int t){while (rc[t]) t=rc[t];return t;}
void del(int pos,int x){
	x=find(pos,x);
	int ls=lc[x],rs=rc[x];
	if (--cnt[x]) return;
	if (!ls && !rs){//不能直接return,先清空一下 
		int &k=rt[pos];
		k=fa[k]=lc[k]=rc[k]=sz[k]=cnt[k]=val[k]=0;
		return;
	}
	if (!ls) rt[pos]=rc[x],fa[rt[pos]]=0;
	else if (!rs) rt[pos]=lc[x],fa[rt[pos]]=0;
	else{
		int lson=Max(ls);swap(ls,lson);
		fa[lson]=0,splay(pos,ls),rc[ls]=rs,fa[rs]=ls,upd(ls);
	}
}
/*int PRE(int t,int x){ if (!t) return 0; if (x<=val[t]) return PRE(lc[t],x); int tmp=PRE(rc[t],x); return tmp?tmp:t; } int pre(int pos,int x){return val[PRE(rt[pos],x)];}*/
int pre(int pos,int x){
	int t=rt[pos];int ans=-inf;
	while (t)
		if (x>val[t]) ans=max(ans,val[t]),t=rc[t];
		else t=lc[t];
	return ans;
}
int nxt(int pos,int x){
	int t=rt[pos];int ans=inf;
	while (t)
		if (x<val[t]) ans=min(ans,val[t]),t=lc[t];
		else t=rc[t];
	return ans;
}
//---------------------------------------------------------线段树
#define mid ((l+r)>>1)//如果没加,这个mid就变二分里的mid了 
void insert(int t,int l,int r,int x,int v){
	ins(t,v);//和下面的修改一样,我刚开始是在l==r的时候插入的 
	if (l==r) return;
	if (x<=mid) insert(t<<1,l,mid,x,v);
	else insert(t<<1|1,mid+1,r,x,v);
	//函数开始的ins()作用跟update()差不多 
}
void change(int t,int l,int r,int x,int v){
	del(t,a[x]),ins(t,v);//刚开始写的是del(t,v) 
	if (l==r) return;
	if (x<=mid) change(t<<1,l,mid,x,v);
	else change(t<<1|1,mid+1,r,x,v);
}
int query(int t,int l,int r,int x,int y,int v){
	if (x<=l && r<=y) return rank(t,v);
// if (x<=l && r<=y) return sz[lc[fi***)]];----会有不存在v的情况,find()会死循环 
	int ans=0;
	if (x<=mid) ans+=query(t<<1,l,mid,x,y,v);
	if (mid<y) ans+=query(t<<1|1,mid+1,r,x,y,v);
	return ans;
}
int Prev(int t,int l,int r,int x,int y,int v){
	if (x<=l && r<=y) return pre(t,v);
	int ans=-inf;
	if (x<=mid) ans=max(ans,Prev(t<<1,l,mid,x,y,v));
	if (mid<y) ans=max(ans,Prev(t<<1|1,mid+1,r,x,y,v));
	return ans;
}
int Succ(int t,int l,int r,int x,int y,int v){
	if (x<=l && r<=y) return nxt(t,v);
	int ans=inf;
	if (x<=mid) ans=min(ans,Succ(t<<1,l,mid,x,y,v));
	if (mid<y) ans=min(ans,Succ(t<<1|1,mid+1,r,x,y,v));
	return ans;
}
#undef mid
int main(){
	n=rd(),m=rd();
	for (i=1;i<=n;i++) a[i]=rd(),insert(1,1,n,i,a[i]);
	for (;m--;){
		opt=rd(),l=rd();
		if (opt!=3) r=rd();
		k=rd();
		if (opt==1) wln(query(1,1,n,l,r,k)+1);
		if (opt==2){
			ll=0,rr=1e8;
			while (ll<=rr){
				mid=ll+rr>>1;
				if (query(1,1,n,l,r,mid)+1<=k) ans=mid,ll=mid+1;
				else rr=mid-1;
				/*if (query(1,1,n,l,r,mid)+1<k) ll=mid+1;------比如查询k=1时query()+1一定>=k,会导致不断向左,输出0 else ans=mid,rr=mid-1; 其实两者本质差别是query()+1==k的时候应该向左还是向右, 如果是小于号的话,rr不断向左,query()也变小,导致答案和标程差很多*/
			}
			wln(ans);
		}
		if (opt==3) change(1,1,n,l,k),a[l]=k;//a[l]=k不能忘 
		if (opt==4) wln(Prev(1,1,n,l,r,k));
		if (opt==5) wln(Succ(1,1,n,l,r,k));
	}
}