luogu P5338 [TJOI2019]甲苯先生的滚榜

背景:

Sir.C ₇ H ₈ \text{Sir.}\text{C}₇\text{H}₈ Sir.CH系列的数据结构的题。

题目传送门:

https://www.luogu.org/problemnew/show/P5338

题意:

求动态的两个关键字 x , y x,y x,y A C AC AC数,罚时)的排名。

思路:

显然用 splay \text{splay} splay
因为 x ≤ 1.5 ∗ 1 0 6 x≤1.5*10^6 x1.5106,因此取一个 z = 1 0 7 x − y z=10^7x-y z=107xy(罚时越少越好,因此是负贡献)即可,就可以做到 s p l a y splay splay只维护一个关键字 z z z了。

注意一些细节。
s e e d seed seed的类型是 unsigned int \text{unsigned int} unsigned int,坑了我好久。
再如,你的 splay \text{splay} splay 左 儿 子 &lt; 根 &lt; 右 儿 子 左儿子&lt;根&lt;右儿子 <<,因此你在统计答案时要删除跟自己 z z z相同的人的影响。

代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
#define LL long long
#define UI unsigned int
using namespace std;
	UI seed;
	int n,m,last=7;
	LL a[2000010];
	struct node{LL d;int fa,c,n,son[2];} tr[2000010];
	int root,len;
void ups(int x)
{
	tr[x].c=tr[tr[x].son[0]].c+tr[tr[x].son[1]].c+tr[x].n;
}
void add(LL d,int fa)
{
	tr[++len]=(node){d,fa,1,1,0};
}
void rot(int x,int w)
{
	int y=tr[x].fa,yy=tr[y].fa;
	tr[y].son[1^w]=tr[x].son[w];
	if(tr[x].son[w]) tr[tr[x].son[w]].fa=y;
	tr[yy].son[tr[yy].son[0]==y?0:1]=x;
	
	tr[x].fa=yy,tr[x].son[w]=y,tr[y].fa=x;
	
	ups(y),ups(x);
}
void splay(int x,int rt)
{
	while(tr[x].fa!=rt)
	{
		int y=tr[x].fa,yy=tr[y].fa;
		if(yy==rt)
		{
			rot(x,tr[y].son[0]==x?1:0);
			continue;
		}
		if(tr[yy].son[0]==y)
		{
			if(tr[y].son[0]==x) rot(y,1),rot(x,1); else rot(x,0),rot(x,1);
			continue;
		}
		if(tr[y].son[1]==x) rot(y,0),rot(x,0); else rot(x,1),rot(x,0); 
	}
	if(!rt) root=x;
}
int findip(int x,LL d)
{
	if(d==tr[x].d) return x;
	if(d<tr[x].d)
		return tr[x].son[0]?findip(tr[x].son[0],d):x;
	else
		return tr[x].son[1]?findip(tr[x].son[1],d):x;
}
void ins(LL d)
{
	if(!root)
	{
		add(d,0);
		root=1;
		return;
	}
	int u=findip(root,d);
	if(tr[u].d==d)
	{
		tr[u].n++;
		ups(u);
		splay(u,0);
		return;
	}
	add(d,u);
	tr[u].son[d<tr[u].d?0:1]=len;
	ups(u);
	splay(len,0);
}
void del(LL d)
{
	int u=findip(root,d);
	splay(u,0);
	if(tr[u].n>1)
	{
		tr[u].n--;
		ups(u);
		return;
	}
	if(!tr[u].son[0]&&!tr[u].son[1])
	{
		root=len=0;
		return;
	}
	if(!tr[u].son[0]&&tr[u].son[1])
	{
		root=tr[u].son[1];
		tr[tr[u].son[1]].fa=0;
		return;
	}
	if(tr[u].son[0]&&!tr[u].son[1])
	{
		root=tr[u].son[0];
		tr[tr[u].son[0]].fa=0;
		return;
	}
	int o=tr[u].son[0];
	while(tr[o].son[1]) o=tr[o].son[1];
	splay(o,u);
	tr[o].son[1]=tr[u].son[1];
	tr[tr[o].son[1]].fa=o;
	root=o;
	tr[o].fa=0;
	ups(o);
}
int findrank(LL d)
{
	int u=findip(root,d);
	splay(u,0);
	return tr[tr[u].son[0]].c+1;
}
int randnum()
{
	seed=seed*17+last;
	return seed%n+1;
}
int main()
{
	int T,x,y;
	scanf("%d",&T);
	while(T--)
	{
		scanf("%d %d %u",&n,&m,&seed);
		root=len=0;
		for(int i=1;i<=n;i++)
			tr[i]=(node){0,0,0,0,0,0};
		for(int i=1;i<=n;i++)
		{
			a[i]=0;
			ins(0);
		}
		for(int i=1;i<=m;i++)
		{
			x=randnum(),y=randnum();
			del(a[x]);
			a[x]+=10000000-y;
			ins(a[x]);
			last=(n-findrank(a[x])-(tr[findip(root,a[x])].n-1));
			printf("%d\n",last);
		}
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值