1. 程式人生 > >[BZOJ3065]帶插入區間K小值

[BZOJ3065]帶插入區間K小值

理解 一次 lib 父親 imu += urn oid 所有

第一次寫外層是平衡樹的樹套樹呢

嘗試去搞替罪羊發現自己無法理解高深技術,於是回來剛旋轉treap

旋轉treap有個性質:插入一個節點並把它旋轉到正確的位置後,這個節點的期望子樹大小是$O(\log_2n)$的

沒有找到資料所以不知道這個是怎麽證的,問zjt和yww也說不知道,但實測插入$10^5$個隨機數,所有點插入後的子樹大小加起來是$2\times 10^6$左右,所以大概是對的吧

所以我們可以使用treap做外層樹,當插入一個節點並把它旋轉到位後,暴力重構旋轉影響到的每一個節點(其實就是一條路徑)

因為要找第$k$小,所以內層樹是權值線段樹

修改就把這個節點到父親的每一個節點的權值線段樹刪除原來的權值,插入新的權值即可

查詢就是把平衡樹拆分成一些點和一些子樹,讓這些東西覆蓋要查詢的區間

要記住垃圾回收,不然內存會吃不消

然後我就不知道怎麽算復雜度了反正能過,treap上插入一個節點期望旋轉多少次啊,求教~

#include<stdio.h>
#include<stdlib.h>
struct seg{
	int l,r,siz;
}t[20000000];
int fix[70010],ch[70010][2],fa[70010],siz[70010],v[70010],rt[70010],p[70010],stk[20000000],tmp[70010],root,top,ttot,stot;
#define lc t[x].l
#define rc t[x].r
#define ls ch[x][0]
#define rs ch[x][1]
#define M 70000
int node(){
	int x;
	if(top==0)
		x=++stot;
	else{
		top--;
		x=stk[top+1];
	}
	lc=rc=t[x].siz=0;
	return x;
}
void add(int p,int v,int l,int r,int&x){
	if(x==0)x=node();
	t[x].siz+=v;
	if(l==r)return;
	int mid=(l+r)>>1;
	if(p<=mid)
		add(p,v,l,mid,lc);
	else
		add(p,v,mid+1,r,rc);
}
void rot(int x){
	int y,z,f,B;
	y=fa[x];
	z=fa[y];
	if(y==root)root=x;
	f=ch[y][0]==x;
	B=ch[x][f];
	fa[x]=z;
	fa[y]=x;
	if(B)fa[B]=y;
	ch[x][f]=y;
	ch[y][f^1]=B;
	if(z)ch[z][ch[z][1]==y]=x;
	z=siz[x];
	siz[x]=siz[y];
	siz[y]-=z-siz[B];
}
int ins(int&x,int p,int d){
	if(x==0){
		x=++ttot;
		v[x]=d;
		fix[x]=rand()*rand();
		siz[x]=1;
		return x;
	}
	siz[x]++;
	int k;
	if(p<=siz[ls]){
		k=ins(ls,p,d);
		fa[ls]=x;
	}else{
		k=ins(rs,p-siz[ls]-1,d);
		fa[rs]=x;
	}
	return k;
}
void rec(int x){
	if(lc)rec(lc);
	if(rc)rec(rc);
	top++;
	stk[top]=x;
}
void dfs(int&rt,int x){
	if(ls)dfs(rt,ls);
	add(v[x],1,0,M,rt);
	if(rs)dfs(rt,rs);
}
void gao(int x){
	if(rt[x])rec(rt[x]);
	rt[x]=0;
	dfs(rt[x],x);
}
void insert(int p,int d){
	int x=ins(root,p,d),f;
	while(fa[x]&&fix[fa[x]]>fix[x]){
		f=fa[x];
		rot(x);
		gao(f);
	}
	gao(x);
	x=fa[x];
	while(x){
		add(d,1,0,M,rt[x]);
		x=fa[x];
	}
}
int getkth(int x,int k){
	while(k!=siz[ls]+1){
		if(k<=siz[ls])
			x=ls;
		else{
			k-=siz[ls]+1;
			x=rs;
		}
	}
	return x;
}
void modify(int p,int d){
	int x=getkth(root,p);
	int r=v[x];
	v[x]=d;
	while(x){
		add(r,-1,0,M,rt[x]);
		add(d,1,0,M,rt[x]);
		x=fa[x];
	}
}
void getrt(int x,int l,int r){
	if(l<=1&&r>=siz[x]){
		p[0]++;
		p[p[0]]=rt[x];
		return;
	}
	if(r<=siz[ls])return getrt(ls,l,r);
	if(l>siz[ls]+1)return getrt(rs,l-siz[ls]-1,r-siz[ls]-1);
	tmp[0]++;
	tmp[tmp[0]]=v[x];
	getrt(ls,l,r);
	getrt(rs,l-siz[ls]-1,r-siz[ls]-1);
}
int query(int l,int r,int k){
	p[0]=tmp[0]=0;
	getrt(root,l,r);
	int L=0,R=M,mid,res,i;
	while(L!=R){
		mid=(L+R)>>1;
		res=0;
		for(i=1;i<=p[0];i++)res+=t[t[p[i]].l].siz;
		for(i=1;i<=tmp[0];i++)if(tmp[i]>=L&&tmp[i]<=mid)res++;
		if(res>=k){
			R=mid;
			for(i=1;i<=p[0];i++)p[i]=t[p[i]].l;
		}else{
			k-=res;
			L=mid+1;
			for(i=1;i<=p[0];i++)p[i]=t[p[i]].r;
		}
	}
	return L;
}
int main(){
	srand(19260817);
	int n,m,i,l,r,k,las;
	char s[5];
	scanf("%d",&n);
	for(i=0;i<n;i++){
		scanf("%d",&k);
		insert(i,k);
	}
	scanf("%d",&m);
	las=0;
	while(m--){
		scanf("%s%d%d",s,&l,&r);
		l^=las;
		r^=las;
		if(s[0]==‘Q‘){
			scanf("%d",&k);
			k^=las;
			las=query(l,r,k);
			printf("%d\n",las);
		}
		if(s[0]==‘M‘)modify(l,r);
		if(s[0]==‘I‘)insert(l-1,r);
	}
}

[BZOJ3065]帶插入區間K小值