【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;
}