题目中的销牌操作看起来不太好理解,但其实,这种操作和环形结构有着异曲同工之妙。我们维护一个变量表示当前牌库顶的牌,每次销牌时,令
,因为是环形结构,所以我们还要对
取模,模数为当前牌数。问题来了,每次销牌过后都要取走牌库顶的牌,因此模拟销牌操作时需要逐一判断当前的牌是否已经被取走(还要开一个
数组),这样显然会超时。这时我们就需要转换思路,我们发现,改变
的值后,我们要取的牌就是当前牌库(假设我们已经取走了以前的牌)的第
张(我也不知道怎么发现的...)。怎么快速求出第
张牌呢?建立一棵权值线段树,在上面二分即可。详情请看代码。
#include<cstdio>
#define maxn 700010
#define reg register
using namespace std;
int n,now;
int num[maxn<<2];//权值线段树
int read()
{
reg char ch=getchar();reg int in=0;
while(ch>'9'||ch<'0') ch=getchar();
while(ch<='9'&&ch>='0') in=(in<<1)+(in<<3)+ch-'0',ch=getchar();
return in;
}
void build(reg int x,reg int l,reg int r)
{
if(l==r)
{
num[x]=1;return;
}
reg int mid=l+r>>1,ls=x<<1,rs=x<<1|1;
build(ls,l,mid),build(rs,mid+1,r);
num[x]=num[ls]+num[rs];
}
int query(reg int x,reg int l,reg int r,reg int k)
{
num[x]--;//查询的同时进行修改,因为取走了一张牌
if(l==r) return l;//找到了第k大
reg int mid=l+r>>1,ls=x<<1,rs=x<<1|1;
if(k<=num[ls]) return query(ls,l,mid,k);//二分查找
return query(rs,mid+1,r,k-num[ls]);//如果第k大在右子结点,记得改变k的值
}
int main()
{
n=read(),build(1,1,n);
for(reg int i=n;i;i--)
{
now=(now+read())%i;
printf("%d\n",query(1,1,n,now+1));
}
return 0;
}
主程序中有一个值得注意的细节:按理说应把的初值赋为
(因为是从第
张牌算起而不是第
张),而且调用查找函数时应用
。如果
那么
,需要特判:
。为了避免讨论,我们把
的初值赋为
,调用查找函数时
即可。