题目链接: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操作来更新子树大小了。