码迷,mamicode.com
首页 > 其他好文 > 详细

[BZOJ3065]带插入区间K小值

时间:2017-12-30 20:25:32      阅读:132      评论:0      收藏:0      [点我收藏+]

标签:查询   modify   void   char s   else   无法   [1]   insert   size   

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

尝试去搞替罪羊发现自己无法理解高深技术,于是回来刚旋转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小值

标签:查询   modify   void   char s   else   无法   [1]   insert   size   

原文地址:https://www.cnblogs.com/jefflyy/p/8150982.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!