P3988 [SHOI2013] 发牌

P3988 [SHOI2013] 发牌

题目描述

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

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

数据规模与约定

对于 100% 的数据,0Ri<N , N7×105

说句闲话:

年底平衡树大甩卖,比模板还简单的平衡树紫题仅需40行,走过路过不要错过!

Solution:

言归正转,我们只需要搞清楚这题的洗牌方式就能愉快的把这题A掉。

以初始序列为例,假设我们要进行 R 次销牌操作:

第0次: 1,2,3...N
第1次: 2,3...N,1
第2次: 2...N,1,2
R 次 :R+1...N,1,2...R

所以“连续进行 R 次销牌操作就相当于把 [1,R] 这个区间取出来然后拼到 N 的后面。那么我们取到的就会是 R+1 这张牌”

但是要注意的是,由于我们牌的序列是动态的,也就是说,我上述所有的标号是其实当前的牌堆序列的下标,而不是最初的标号。所以我并不认为线段树好写,所以我选了FHQ。

Code:

#include<bits/stdc++.h>
const int N=7e5+5;
using namespace std;
int n,m,cnt,rt;
struct Tree{
int ls,rs,val,siz,rev,pri;
}t[N];
inline int rd(){return rand()*rand()+17;}
inline int Node(int val){t[++cnt]={0,0,val,1,0,rd()};return cnt;}
inline void pushup(int x){t[x].siz=t[t[x].ls].siz+t[t[x].rs].siz+1;}
inline void splite(int x,int &a,int &b,int k)
{
if(!x){a=b=0;return;}int tmp=t[t[x].ls].siz+1;
if(k>=tmp){a=x;splite(t[x].rs,t[x].rs,b,k-tmp);}
else {b=x;splite(t[x].ls,a,t[x].ls,k);}
pushup(x);
}
int merge(int x,int y)
{
if(!x||!y)return x|y;
if(t[x].pri<t[y].pri){t[x].rs=merge(t[x].rs,y);pushup(x);return x;}
else {t[y].ls=merge(x,t[y].ls);pushup(y);return y;}
}
void work()
{
cin>>n;
for(int i=1;i<=n;i++){rt=merge(rt,Node(i));}
for(int i=1,x,a,b,c;i<=n;i++)
{
scanf("%d",&x);x=x%t[rt].siz+1;
splite(rt,a,b,x-1);splite(b,b,c,1);
printf("%d\n",t[b].val);
rt=merge(c,a);
}
}
int main()
{
work();
return 0;
}
posted @   liuboom  阅读(9)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
点击右上角即可分享
微信分享提示