[BZOJ3065]带插入区间K小值

Posted jefflyy

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[BZOJ3065]带插入区间K小值相关的知识,希望对你有一定的参考价值。

第一次写外层是平衡树的树套树呢

尝试去搞替罪羊发现自己无法理解高深技术,于是回来刚旋转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小值的主要内容,如果未能解决你的问题,请参考以下文章

[BZOJ3065]带插入区间K小值 解题报告 替罪羊树+值域线段树

Bzoj3065 带插入区间K小值

bzoj 3065 带插入区间k小值

BZOJ3065 带插入区间K小值

[BZOJ3065]带插入区间K小值

[bzoj3065] 带插入区间第k小值 [重量平衡树套线段树]