平衡树(treap或splay?)

推荐想学的同学别看我的博客 学!别人写的会清楚很多,我是没有讲解的。很烂的

呃几乎是最后一个专题了罢,不知不觉间便也是三年,三年,大抵也后悔过,放弃过,麻木过,颓唐过,失望过。不过反正总还能跌跌撞撞的走下去。也许,这次noip,我不知道做了多少准备。也许很多也许很少,也许拼尽全力也许不值一提。这一切都不重要了。反正结束亦是开始,命运的齿轮倒也还能在一切消逝前转着。得承认的,我不是什么好东西,也没有多聪明,懒,爱抱怨,老想着结束。可最终这样再看看,路上的风风雨雨,平平淡淡,每一个无法预期的清晨与夜晚,却也让人怀着期待,怀着清爽。无论是来的路上,又或是离的路上,无可替代的,是那些走来又走过的人们,是不是想过后悔呢?大概是有的,但是!绕远的路,总有风景。哈哈哈时间不多了,别怕,我们会走到最后。
咳咳咳!来看看这个平衡树,我们将用一天半的时间将其搞定。P3369 【模板】普通平衡树,那么其实这东西操作不多,我其实也不是很想学这个,感觉没有treap好用的说。splay

#include<bits/stdc++.h>
using namespace std;
int n,m;
struct node 
{
	int lc,rc,val,cnt,tk,sz;
};
node tr[1100010];
int tot=0,root;
int bn(int val)
{
	tot++;
	tr[tot].val=val;
	tr[tot].tk=rand()%10000011;
	tr[tot].cnt=tr[tot].sz=1;
	tr[tot].lc=tr[tot].rc=0;
	return tot;
}

void up(int p)
{
	tr[p].sz=tr[tr[p].lc].sz+tr[tr[p].rc].sz+tr[p].cnt;
	return ;
}
int yx(int p)
{
	int q=tr[p].lc;
	tr[p].lc=tr[p].rc;
	tr[q].rc=p;
	tr[q].sz=tr[p].sz;
	up(p);
	return q;
}
int zx(int p)
{
	int q=tr[p].rc;
	tr[p].rc=tr[p].lc;
	tr[q].lc=p;
	tr[q].sz=tr[p].sz;
	up(p);
	return q;
}
void add(int &p,int val)
{
	if(!p) 
	{
		p=bn(val);
		return ;
	} 
	tr[p].sz++;
	if(val==tr[p].val) 
	{
		tr[p].cnt++;
		return ;
	}
	if(val<tr[p].val)
	{
		add(tr[p].lc,val);
		if(tr[p].tk<tr[tr[p].lc].tk) yx(p);
	}
	else 
	{
		add(tr[p].rc,val);
		if(tr[p].tk<tr[tr[p].rc].tk) zx(p);
	}
	return ;
}
void dl(int &p,int val)
{
	if(!p) return ;
	tr[p].sz--;
	if(val==tr[p].val) 
	{
		if(tr[p].cnt>1) 
		{
			tr[p].cnt--;
			return ;
		}
		if(!tr[p].lc||!tr[p].rc) p=tr[p].lc+tr[p].rc;
		else if(tr[tr[p].lc].tk>tr[tr[p].rc].tk)
		{
			yx(p);
			dl(tr[p].rc,val);
		}
		else
		{
			zx(p);
			dl(tr[p].lc,val);
		}
		return ;
	}
	if(val<tr[p].val) dl(tr[p].lc,val);
	else dl(tr[p].rc,val);
	return ; 
}
int findmax(int val)
{
	int p=root,ans=0;
	while(p)
	{
		if(tr[p].val<val)
		{
			ans=tr[p].val;
			p=tr[p].rc;
		}
		else p=tr[p].lc;
	}
	return ans;
}
int findmin(int val)
{
	int p=root,ans=0;
	while(p)
	{
		if(tr[p].val>val)
		{
			ans=tr[p].val;
			p=tr[p].lc;
		}
		else p=tr[p].rc;
	}
	return ans;	
}
int findnum(int p,int val)
{
//	printf("*");
	if(!p) return 0;
	if(tr[p].val==val) return tr[tr[p].lc].sz+1;
	else if(val<tr[p].val) return findnum(tr[p].lc,val);
	else return findnum(tr[p].rc,val)+tr[tr[p].lc].sz+tr[p].cnt;
}
int findval(int p,int k)
{
	if(!p) return 0;
	if(tr[tr[p].lc].sz>=k) return findval(tr[p].lc,k);
	if(tr[tr[p].lc].sz+tr[p].cnt>=k) return tr[p].val;
	return findval(tr[p].rc,k-tr[tr[p].lc].sz-tr[p].cnt);
}
int main()
{
//	freopen("phs.out","w",stdout);
	for (int i=1;i<=1000;i++) tr[i].lc=tr[i].rc=tr[i].val=tr[i].cnt=tr[i].tk=tr[i].sz=0;
	root=0;
	scanf("%d",&n);
	while(n--)
	{
		int pd;
		scanf("%d",&pd);
		if(pd==1) 
		{
			int val;
			scanf("%d",&val);
			add(root,val);
		}
		else if(pd==2)
		{
			int val;
			scanf("%d",&val);
			dl(root,val);
		}
		else if(pd==3)
		{
			int val;
			scanf("%d",&val);
			printf("%d\n",findnum(root,val));
		}
		else if(pd==4)
		{
			int val;
			scanf("%d",&val);
			printf("%d\n",findval(root,val));
		}
		else if(pd==5)
		{
			int x;
			scanf("%d",&x);
			printf("%d\n",findmax(x));
		}
		else 
		{
			int x;
			scanf("%d",&x);
			printf("%d\n",findmin(x));
		}
	}
	return 0;
}

我打一遍再说吧。(=,=)其实还是想学treap的splay的左右旋转操作好难啊。草我还是学一下,treap吧,害。麻烦呜呜呜呜。下定决心了,刚刚浪费了半天考虑学什么呜呜呜我是智障。想ta。
嗯treap的话我推荐是无旋的那种,明天早上写。 注意一下这个打法中merge是的先后顺序是十分重要的,因为有大小之分。导致于我调了半天呜呜呜其他重要的东西放代码里。推荐:https://www.bilibili.com/video/BV1ft411E7JW?。p=1&vd_source=f51708a79de2172437e75278f8c832e0的视频。学习
在这里插入图片描述

#include<bits/stdc++.h>
using namespace std;
int n,m,len=0,root;
struct treap
{
	int l,r,val,key,cnt,siz;
};treap tr[100001];
int btnew(int val)//新开一个节点 
{
	tr[++len].val=val;tr[len].key=rand();
	tr[len].siz=1;
	return len;
}
void update(int now)
{
	tr[now].siz=tr[tr[now].l].siz+tr[tr[now].r].siz+1;
	return ; 
 } 
void split(int now,int val,int &x,int &y)//根据子树的点值来分裂,一半是<=的,另一半是大于的 
{
	if(!now) x=y=0;//如果我这个节点不存在 
	else 
	{
		if(tr[now].val<=val) x=now,split(tr[now].r,val,tr[now].r,y);
		//若我这个节点的值小于等于val,那么我左子树肯定也,不过右子树还要再看看 
		//所以将右子树大小传进去 
		else y=now,split(tr[now].l,val,x,tr[now].l); 
		//同上,问题在于它会更更新部分节点的左右儿子,使得整棵树完全分裂 
		update(now);
	}
	return ;
}
int merge(int x,int y) //合并两棵树,保证了x里面所有的元素<y的 。 
{
	if(!x||!y) return x+y;//如果其中有一个不存在 
	//维护二叉堆的性质 
	if(tr[x].key>tr[y].key)//这样理解:key维护上下,val维护左右 
	{
		//那么这个就是右下,但可能原来有了,所以要合并 
		tr[x].r=merge(tr[x].r,y); 
		update(x);return x;	
	}	
	else
	{
		//合并到左下 
		tr[y].l=merge(x,tr[y].l);
		update(y);return y;	
	} 
} 
void ins(int val)
{
	int x,y;split(root,val,x,y);//先将val拆走,那么直接插入即可 
	root=merge(merge(x,btnew(val)),y);//注意了要分先后顺序啊 
	return ; 
}

void dl(int val)//删除 
{
	int x,y,z;split(root,val,x,z);split(x,val-1,x,y);//分离val-1与val
	//剩下的就是值为val的,发现可能不止一个。那么我们再合并其子树就行 
	y=merge(tr[y].l,tr[y].r); 
	root=merge(merge(x,y),z);
	return ;
}
int getrank(int val)
{
	int x,y,rt;split(root,val-1,x,y);//查排名当然简单 
	rt=tr[x].siz+1;root=merge(x,y);
	return rt;
}
int getval(int rank)//根据排名找值 
{
	int now=root;
	while(now)
	{
		if(tr[tr[now].l].siz+1==rank) break;//根直接就是 
		else if(tr[tr[now].l].siz>=rank) now=tr[now].l;//往左走的时候 
		else rank-=(tr[tr[now].l].siz+1),now=tr[now].r;//往右走的时候要减去左边的子树 
	}	
	return tr[now].val;
}
int getper(int val)//1到val-1中最大的数(将val-1)分离出去 
{
	int x,y;split(root,val-1,x,y);
	int now=x;while(tr[now].r) now=tr[now].r;//找最大 
	int rt=tr[now].val;root=merge(x,y);//合并啊啊啊 
	return rt;
}
int getnext(int val)//val+1到n最大的数 (将val分离出去) 
{
	int x,y;split(root,val,x,y);
	int now=y;while(tr[now].l) now=tr[now].l;//最小 
	int rt=tr[now].val;root=merge(x,y);//记住要合并 
	return rt;
}
int main()
{
	int t;scanf("%d",&t);
	while(t--)
	{
		int op;scanf("%d",&op);
		if(op==1)
		{
			int x;scanf("%d",&x);
			ins(x);
		}
		else if(op==2)
		{
			int x;scanf("%d",&x);
			dl(x);
		}
		else if(op==3)
		{
			int x;scanf("%d",&x);
			printf("%d\n",getrank(x));
		}
		else if(op==4)
		{
			int x;scanf("%d",&x);
			printf("%d\n",getval(x));
		}
		else if(op==5)
		{
			int x;scanf("%d",&x);
			printf("%d\n",getper(x));
		}
		else 
		{
			int x;scanf("%d",&x);
			printf("%d\n",getnext(x));
		}
	}
	return 0;
}

P2234 [HNOI2002]营业额统计明显的我们需要维护比,当前x小的最大值,比当先x大的最小值。

#include<bits/stdc++.h>
#define int long long 
using namespace std;
int n,m,len=0,root;
struct treap
{
	int siz,val,key,l,r;
};treap tr[1000001];
int btnew(int val)
{
	int now=++len;
	tr[now].val=val;tr[now].key=rand();tr[now].siz=1;
	return now;	
}
void update(int now)
{
	tr[now].siz=tr[tr[now].l].siz+tr[tr[now].r].siz+1;
	return ;
}
void split(int now,int val,int &x,int &y)
{
	if(!now) x=y=0;
	else 
	{
		if(tr[now].val<=val) x=now,split(tr[now].r,val,tr[now].r,y);
		else y=now,split(tr[now].l,val,x,tr[now].l);
	}
	return ; 
}
int merge(int x,int y)
{
	if(!x||!y) return x+y;
	if(tr[x].key>tr[y].key)
	{
		tr[x].r=merge(tr[x].r,y);
		update(x);return x;
	}
	else 
	{
		tr[y].l=merge(x,tr[y].l);
		update(y);return y;
	}
}
void ins(int val)
{
	int x,y;split(root,val-1,x,y);
	root=merge(merge(x,btnew(val)),y);
	return ;
}
int getper(int val)
{
	int x,y,z;split(root,val,x,y);
	int now=x;while(tr[now].r) now=tr[now].r;
	int rt=tr[now].val;root=merge(x,y);
	return rt;
}
int getnext(int val)
{
	int x,y,z;split(root,val,x,y);
	int now=y;while(tr[now].l) now=tr[now].l;
	int rt=tr[now].val;root=merge(x,y);
	return rt;
}
signed main()
{
	scanf("%lld",&n);ins(1e18);ins(-1e18);int ans=0;
	for(int i=1;i<=n;i++)
	{
		int x;scanf("%lld",&x);
		if(i==1) ans+=x;
		else ans+=min(x-getper(x),getnext(x)-x);
		ins(x);
	}printf("%lld",ans);
	return 0;
}

P1486 [NOI2004] 郁闷的出纳员嗯!

#include<bits/stdc++.h>
#define int long long 
using namespace std;
int n,m,len=0,saygb=0,minn,root;
struct treap 
{
	int siz,val,key,l,r;
};treap tr[3000001];
int btnew(int val)
{
	int now=++len;
	tr[now].val=val,tr[now].key=rand(),tr[now].siz=1;
	return now;
}
void update(int now)
{
	tr[now].siz=tr[tr[now].l].siz+tr[tr[now].r].siz+1;
	return ;
}
void split(int now,int val,int &x,int &y)
{
	if(!now) x=y=0;
	else 
	{
		if(tr[now].val<=val) x=now,split(tr[now].r,val,tr[now].r,y);//明显的tr[now].r<y 
		else y=now,split(tr[now].l,val,x,tr[now].l);
		update(now);
	}
	return ;
}
int merge(int x,int y)
{
	if(!x||!y) return x+y;
	if(tr[x].key>tr[y].key)
	{
		tr[x].r=merge(tr[x].r,y);
		update(x);return x;
	}
	else 
	{
		tr[y].l=merge(x,tr[y].l);
		update(y);return y;
	}
}
void ins(int val)
{
	int x,y;split(root,val,x,y);
	root=merge(merge(x,btnew(val)),y);
	return ;
}
int getval(int rank)
{
	int now=root;
	while(now)
	{
		if(tr[tr[now].l].siz+1==rank) break ;
		else if(tr[tr[now].l].siz>=rank) now=tr[now].l;
		else rank-=tr[tr[now].l].siz+1,now=tr[now].r;
	}
	return tr[now].val;
}
void dl(int val)
{
	int x,y,z;split(root,val,x,y);
	root=y;saygb+=tr[x].siz;
	return ;
}
signed main()
{
	scanf("%lld%lld",&n,&minn);int sum=0;
	while(n--)
	{
		char op[10];scanf("%s",op+1);
		if(op[1]=='I')  	
		{
			int x;scanf("%lld",&x);
			if(x>=minn) x-=sum,ins(x);
		}
		else if(op[1]=='A')
		{
			int k;scanf("%lld",&k);sum+=k;
		}
		else if(op[1]=='S')
		{
			int k;scanf("%lld",&k);sum-=k;
			dl(minn-sum-1);
		}
		else 
		{ 
			int x;scanf("%lld",&x);
			if(tr[root].siz>=x) printf("%lld\n",getval(tr[root].siz-x+1)+sum);
			else printf("-1\n");
		}
	}
	printf("%lld",saygb);
	return 0;
}

P3850 [TJOI2007]书架排名分裂的好题。练习了一下,哦对提一下,排名分裂与权值分裂是两种分裂方法。我们上面写的都是按照权分裂的,那么这一题不好用按权分裂的方法,于是我们选择排名分裂。那么分裂的时候就要注意了!

#include<bits/stdc++.h>
using namespace std;
int n,m,len=0,root;
struct treap
{
	int siz,val,key,lc,rc;
};treap tr[1000001];
string s[1000001];
void update(int now)
{
	tr[now].siz=tr[tr[now].lc].siz+tr[tr[now].rc].siz+1;
	return ;
}
int btnew(int val)
{
	int now=++len; 
	tr[now].val=val,tr[now].key=rand(),tr[now].siz=1;
	return now;
}
void split(int now,int siz,int &x,int &y)
{
	if(!now) x=y=0;
	else 
	{
		if(tr[tr[now].lc].siz>=siz) y=now,split(tr[now].lc,siz,x,tr[now].lc);
		else x=now,split(tr[now].rc,siz-tr[tr[now].lc].siz-1,tr[now].rc,y);
		update(now);
	}
	return ;
}
int merge(int x,int y)
{
	if(!x||!y) return x+y;
	if(tr[x].key>tr[y].key) 
	{
		tr[x].rc=merge(tr[x].rc,y);
		update(x);return x;
	}
	else 
	{
		tr[y].lc=merge(x,tr[y].lc);
		update(y);return y;
	}
}
void ins(int val,int id)
{
	int x,y;split(root,val,x,y);
	root=merge(merge(x,btnew(id)),y);
	return;
}
void findnum(int val)
{
	int r1,r2,r3,r4;split(root,val,r1,r2);split(r2,1,r3,r4);
	cout<<s[tr[r3].val]<<endl;
	root=merge(r1,merge(r3,r4));
	return ;
}
int main()
{
	scanf("%d",&n);int cnt=0;
	for(int i=1;i<=n;i++)
	{
		cin >> s[++cnt];ins(i-1,cnt);
	}scanf("%d",&m);
	for(int i=1;i<=m;i++)
	{
		cin >> s[++cnt];int x;scanf("%d",&x);
		ins(x,cnt);
	}int q;scanf("%d",&q);
	while(q--)
	{
		int x;scanf("%d",&x);
		findnum(x);
	}
	return 0;
} 

P3391 【模板】文艺平衡树啊这个其实就维护一个区间,那么我们考虑从lazy入手,那么发现区间反转的操作(其实线段树的操作也一样)就只是交换左右两个儿子。那么再通过一个大小排序维护,即可。

#include<bits/stdc++.h>
using namespace std;
int n,m,len=0,root=0;
struct treap
{
	int siz,val,key,lc,rc,lazy;
};treap tr[100001];
int btnew(int val)
{
	int now=++len;
	tr[now].siz=1,tr[now].val=val,tr[now].key=rand(),tr[now].lazy=0;
	return now;
}
void update(int now)
{
	tr[now].siz=tr[tr[now].lc].siz+tr[tr[now].rc].siz+1;
	return ;
}
void pushdown(int now)
{
	if(!tr[now].lazy) return ;//printf("*");
	swap(tr[now].lc,tr[now].rc);tr[now].lazy=0;
	tr[tr[now].lc].lazy^=1,tr[tr[now].rc].lazy^=1;
	return ;
}
void split(int now,int siz,int &x,int &y)
{
	if(!now) x=y=0;
	else 
	{
		pushdown(now);
		if(tr[tr[now].lc].siz<siz) x=now,split(tr[now].rc,siz-tr[tr[now].lc].siz-1,tr[now].rc,y);
		else y=now,split(tr[now].lc,siz,x,tr[now].lc);
		update(now);
	}
	return ;
}


int merge(int x,int y)
{
	if(!x||!y) return x+y;
	if(tr[x].key>tr[y].key)
	{
		pushdown(x); 
		tr[x].rc=merge(tr[x].rc,y);
		update(x);return x;
	}
	else 
	{
		pushdown(y); 
		tr[y].lc=merge(x,tr[y].lc);
		update(y);return y;
	}  
}
void re(int l,int r)
{ 
	int x,y,z;split(root,l-1,x,y);split(y,r-l+1,y,z);
	tr[y].lazy^=1;root=merge(merge(x,y),z);
	return ;  	
}
void ldr(int now)
{
	if(!now) return ;pushdown(now);
	ldr(tr[now].lc);printf("%d ",tr[now].val);ldr(tr[now].rc);
	return ;
}
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++) root=merge(root,btnew(i));
	while(m--)
	{
		int l,r;scanf("%d%d",&l,&r);
		re(l,r);
	}
	ldr(root); 
	return 0;
}

P3224 [HNOI2012]永无乡
在这里插入图片描述
呃启发式合并确实离谱啊,不过其实大部分的合并题目都应该想到这一个,合并并查集,连通块等东西都可以用这一手。然后因为根的问题调了一会。

#include<bits/stdc++.h>
using namespace std;
int n,m,len=0;
int root[1000001];
struct treap
{
	int lc,rc,siz,val,key;
};treap tr[1000001];
int findroot(int x)
{
	if(root[x]==x) return x;
	return root[x]=findroot(root[x]);
}
int btnew(int val)
{
	int now=++len;
	tr[now].key=rand(),tr[now].siz=1,tr[now].val=val;
	return now;
}
void update(int now)
{
	tr[now].siz=tr[tr[now].lc].siz+tr[tr[now].rc].siz+1; 
	return ;
}
void split(int now,int val,int &x,int &y)
{
	if(!now) x=y=0;
	else 
	{
		if(tr[now].val<=val) x=now,split(tr[now].rc,val,tr[now].rc,y);
		else y=now,split(tr[now].lc,val,x,tr[now].lc);	
		update(now);
	}
	return ;
}
int merge(int x,int y)
{
	if(!x||!y) return x+y;
	if(tr[x].key>tr[y].key) 
	{
		tr[x].rc=merge(tr[x].rc,y);
		update(x);return x;  
	}
	else 
	{
		tr[y].lc=merge(x,tr[y].lc);
		update(y);return y;  
	}
}
int ins(int x,int y)
{
	int a,b,rt;split(x,tr[y].val,a,b);	
	return merge(merge(a,y),b);
}
void dfs(int &x,int y)
{
	if(!y) return ;
	dfs(x,tr[y].lc);dfs(x,tr[y].rc);
	tr[y].lc=tr[y].rc=0;x=ins(x,y);
//	update(x);
	return ;
}
int hb(int x,int y)
{
	if(tr[x].siz<tr[y].siz) swap(x,y);
	dfs(root[x],y);
	return root[x];
}
int getval(int x,int rank)
{	
	int now=x;
	while(now)
	{
		if(tr[tr[now].lc].siz+1==rank) break;
		else if(tr[tr[now].lc].siz>=rank) now=tr[now].lc;
		else rank-=tr[tr[now].lc].siz+1,now=tr[now].rc;
//		printf("<<%d %d\n",now,tr[now].siz);
	}
	return now;
}
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++) 
	{
		int x;scanf("%d",&x);
		root[i]=btnew(x);
	}
	for(int i=1;i<=m;i++)
	{
		int x,y,fx,fy;scanf("%d%d",&x,&y);fx=findroot(x),fy=findroot(y);
		if(fx==fy) continue ;
		int z=hb(root[x],root[y]);root[fx]=root[fy]=root[z]=z;
	}
	int q;scanf("%d",&q);
	while(q--)
	{
		char op[10];scanf("%s",op+1);
		if(op[1]=='Q')
		{
			int x,rank;scanf("%d%d",&x,&rank);x=findroot(x);
			if(tr[x].siz>=rank) printf("%d\n",getval(x,rank));
			else printf("-1\n");	
		}
		else 	
		{
			int x,y,fx,fy;scanf("%d%d",&x,&y);fx=findroot(x),fy=findroot(y);
			if(fx==fy) continue ;
			int z=hb(root[x],root[y]);root[fx]=root[fy]=root[z]=z;
		}
	}
	return 0;
} 

再写一题就开始复习了P2042 [NOI2005] 维护数列,实现起来很麻烦的。维护区间标记啊,反转啊,还要用队列保存可用的节点。尽量写写吧,比较这个专题快结束了。
谈一谈插入,这个插入不简单,nlogn的插入已经被淘汰了!我们直接建树然后再插入就可以达到n的复杂度?据说是按照笛卡尔树的建树方式,不过算了学不来。
那么,拜拜~

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值