[HNOI2004]宠物收养场 题解

文章描述了一种使用FHQTreap数据结构解决编程竞赛中的问题,该问题涉及到在人和狗的数据集中找到特定值的前驱和后继,并根据需求进行插入和删除操作。通过一棵平衡树,结合标记来管理空树,实现了高效的操作过程。
摘要由CSDN通过智能技术生成

题目链接:https://www.luogu.com.cn/problem/P2286

大体思路

不难看出,此题对我们的要求就是找出指定值的前驱和后继。之后,再按照题目要求记录相关值。很容易想到的使用两棵平衡树分别记录顾客和狗的相关值。实际上还有一种更加巧妙的思路。因为两棵平衡树当中始终有一棵是空的,所以说可以只用一个标记来记录哪颗平衡树是空的,然后用一棵平衡树来记录即可。

具体实现

这里用的是两棵 F H Q   T r e a p FHQ~Treap FHQ Treap的实现方法。

#include<cstdio>
#include<ctime>
#include<random>
#define MOD 1000000
#define f_inline inline __attribute__((always_inline))
using namespace std;
int cnt[2],root[2];//0:人 1:狗
struct node
{
	int ls,rs,pri,key;
	node(register const int _pri,register const int _key)
	:ls(0),rs(0),pri(_pri),key(_key)
	{}
	node()=default;
}tr[2][80005];
f_inline void newNode(register const int num,register const  int x)
{
	tr[num][++cnt[num]]=node(rand(),x);
}
void Split(register const int num,register const int u,register const int x,register int &L,register int &R)
{
	if(!u)
	{
		L=R=0;
		return;
	}
	if(tr[num][u].key<=x)
	{
		L=u,
		Split(num,tr[num][u].rs,x,tr[num][u].rs,R);
	}
	else
	{
		R=u,
		Split(num,tr[num][u].ls,x,L,tr[num][u].ls);
	}
	//Update(u);
}
int Merge(register const int num,register const int L,register const int R)
{
	if(!(L&&R))
		return L+R;
	if(tr[num][L].pri>tr[num][R].pri)
	{
		tr[num][L].rs=Merge(num,tr[num][L].rs,R);
		//Update(L);
		return L;
	}
	tr[num][R].ls=Merge(num,L,tr[num][R].ls);
	//Update(R);
	return R;
}
f_inline void Insert(register const int num,register const int x)
{
	register int L,R;
	Split(num,root[num],x,L,R),
	newNode(num,x),
	root[num]=Merge(num,Merge(num,L,cnt[num]),R);
}
f_inline void Del(register const int num,register const int x)
{
	register int L,p,R;
	Split(num,root[num],x,L,R),
	Split(num,L,x-1,L,p),
	p=Merge(num,tr[num][p].ls,tr[num][p].rs),
	root[num]=Merge(num,Merge(num,L,p),R);
}
//f_inline void updMin(register int &Sou,register const int x)
//{
//	if(x<Sou)
//		Sou=x;
//}
f_inline int Clo(register const int num,register const int x)//选的是num,值为x
{
	register int L,R;
	Split(num,root[num],x,L,R);
	if(!L)
	{
		root[num]=Merge(num,L,R),
		Split(num,root[num],x-1,L,R);
		register int u(R);
		while(tr[num][u].ls)
			u=tr[num][u].ls;
		register const int ans(tr[num][u].key);
		root[num]=Merge(num,L,R),
		Del(num,ans);
		return (ans-x)%MOD;
	}
	register int u(L);
	while(tr[num][u].rs)
		u=tr[num][u].rs;
	register const int ans1(tr[num][u].key);
	root[num]=Merge(num,L,R),
	Split(num,root[num],x-1,L,R);
	if(!R)
	{
		root[num]=Merge(num,L,R),
		Del(num,ans1);
		return (x-ans1)%MOD;
	}
	u=R;
	while(tr[num][u].ls)
		u=tr[num][u].ls;
	register const int ans2(tr[num][u].key);
	root[num]=Merge(num,L,R);
	register const int res1(x-ans1),res2(ans2-x);
	if(res1<res2)//既有前驱也有后继
	{
		Del(num,ans1);
		return res1%MOD;
	}
	else if(res1>res2)
	{
		Del(num,ans2);
		return res2%MOD;
	}
	else//res1==res2
	{
		Del(num,ans1);
		return res1%MOD;
	}
}
int main()
{
	srand(time(nullptr));
	register int n,cnt_m(0),cnt_d(0),ans(0);//分别记录剩余的人和狗的数量
	scanf("%d",&n);
	while(n--)
	{
//		//debug
//		printf("now:n=%lu ans=%lu\n",n,ans);
//		//end
		register int x,y;
		scanf("%d%d",&x,&y);
		if(x)//来了一个人
		{
			if(!cnt_d)
				Insert(0,y),
				++cnt_m;
			else
				(ans+=Clo(1,y))%=MOD,
				--cnt_d;
		}
		else//来了一个狗
		{
			if(!cnt_m)
				Insert(1,y),
				++cnt_d;
			else
				(ans+=Clo(0,y))%=MOD,
				--cnt_m;
		}
	}
	printf("%d",ans);
	return 0;
}

由于题目中并没有询问第 k k k大的数和排名,并不需要维护每棵子树的 s i z e size size的值,也不需要进行 u p d a t e update update操作来更新子树大小了。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值