【JZOJ3292】【BZOJ4415】【luoguP3988】发牌

description

在一些扑克游戏里,如德州扑克,发牌是有讲究的。一般称呼专业的发牌手为荷官。荷官在发牌前,先要销牌(burn card)。所谓销牌,就是把当前在牌库顶的那一张牌移动到牌库底,它用来防止玩家猜牌而影响游戏。

假设一开始,荷官拿出了一副新牌,这副牌有N张不同的牌,编号依次为1到N。由于是新牌,所以牌是按照顺序排好的,从牌库顶开始,依次为1, 2,……直到N,N号牌在牌库底。为了发完所有的牌,荷官会进行N次发牌操作,在第i次发牌之前,他会连续进行Ri次销牌操作,Ri由输入给定。请问最后玩家拿到这副牌的顺序是什么样的?

举个例子,假设N = 4,则一开始的时候,牌库中牌的构成顺序为{1, 2, 3, 4}。

假设R1=2,则荷官应该连销两次牌,将1和2放入牌库底,再将3发给玩家。目前牌库中的牌顺序为{4, 1, 2}。

假设R2=0,荷官不需要销牌,直接将4发给玩家,目前牌库中的牌顺序为{1,2}。

假设R3=3,则荷官依次销去了1, 2, 1,再将2发给了玩家。目前牌库仅剩下一张牌1。

假设R4=2,荷官在重复销去两次1之后,还是将1发给了玩家,这是因为1是牌库中唯一的一张牌。


analysis

  • 你以为是\(splay\)?线段树!

  • 线段树维护每段区间有几个可用的数

  • 然后线段树上类似\(splay\)一样二分查询就好了

  • 注意原指针的位置


code

#include<stdio.h>
#include<string.h>
#include<algorithm>
#define MAXN 700005
#define ll long long
#define fo(i,a,b) for (ll i=a;i<=b;++i)
#define fd(i,a,b) for (ll i=a;i>=b;--i)

using namespace std;

ll tr[MAXN<<2];
ll n,m,now=1;

inline ll read()
{
	ll x=0,f=1;char ch=getchar();
	while (ch<'0' || '9'<ch){if (ch=='-')f=-1;ch=getchar();}
	while ('0'<=ch && ch<='9')x=x*10+ch-'0',ch=getchar();
	return x*f;
}
inline void update(ll t)
{
	tr[t]=tr[t<<1]+tr[(t<<1)+1];
}
inline void build(ll t,ll l,ll r)
{
	if (l==r){tr[t]=1;return;}
	ll mid=(l+r)>>1;
	build(t<<1,l,mid),build((t<<1)+1,mid+1,r);
	update(t);
}
inline void find(ll t,ll l,ll r,ll x)
{
	if (l==r){tr[t]=0,printf("%lld\n",l);return;}
	ll mid=(l+r)>>1;
	if (x<=tr[t<<1])find(t<<1,l,mid,x);
	else find((t<<1)+1,mid+1,r,x-tr[t<<1]);
	update(t);
}
int main()
{
	freopen("T1.in","r",stdin);
	n=m=read(),build(1,1,n);
	fo(i,1,n)
	{
		ll x=read()%m;now=now+x>m?(now+x)%m:now+x;
		find(1,1,n,now),--m,now=now>m?1:now;
	}
	return 0;
}
posted @ 2019-07-02 15:58  路人黑的纸巾  阅读(123)  评论(0编辑  收藏  举报