题目地址:http://poj.org/problem?id=2886
/*
Author:Bob Lee
2012.9.26
=====================================================================
题目:N 个小孩围成一圈,他们被顺时针编号为 1 到 N。
每个小孩手中有一个卡片,上面有一个非 0 的数字,
游戏从第 K 个小孩开始,他告诉其他小孩他卡片上的数字并离开这个圈,
他卡片上的数字 A 表明了下一个离开的小孩,
如果 A 是大于 0 的,则下个离开的是左手边第 A 个,
如果是小于 0 的,则是右手边的第 -A 个小孩。
游戏将直到所有小孩都离开,在游戏中,
第 p 个离开的小孩将得到 F(p) 个糖果,F(p) 是 p 的约数的个数,
问谁将得到最多的糖果。输出最幸运的小孩的名字和他可以得到的糖果。
=====================================================================
思路:
反素数:
对 x来说约束个数 G(x),如果 对于 i<x 有 G(i)<G(x)
则称x为反素数。
那么这个题目里面就是要求出最大的反素数
这个是可以打表的,但是我们不知道到底是谁在这个数出去的
我们就需要用到模拟
另外,我们要引进一个初始值0
第0个孩子的num也为0
方便我们后面的操作
对于num>0,是它后面的,由于它本身也要已经是要删掉了的,所以
k = k+num-1
else
k = k+num
*/
#include<cstdio>
#include<iostream>
#include<cstring>
#include<string>
using namespace std;
#define maxn 500005
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
int sum[maxn<<2];
int lastpos;
int RPrime[]={//反素数
1,2,4,6,12,24,36,48,60,120,180,240,360,720,840,1260,1680,2520,5040,7560,10080,15120,
20160,25200,27720,45360,50400,55440,83160,110880,166320,221760,277200,332640,498960,
554400
};
int fact[]={//反素数约数个数
1,2,3,4,6,8,9,10,12,16,18,20,24,30,32,36,40,48,60,64,72,80,84,90,96,100,108,120,128,
144,160,168,180,192,200,216
};
struct c
{
char name[20];
int num;
}child[maxn];
void PushUp(int rt)
{
sum[rt] = sum[rt<<1]+sum[rt<<1|1];
}
void build(int l,int r,int rt)
{
if(l == r)
{
sum[rt] = 1;
return;
}
int m = (l+r)>>1;
build(lson);
build(rson);
PushUp(rt);
}
void update(int p,int l,int r,int rt)
{
sum[rt]--;
if(l == r)
{
lastpos = l;
return;
}
int m = (l+r)>>1;
if(p <= sum[rt<<1])
update(p,lson);
else
update(p-sum[rt<<1],rson);
}
int main()
{
int n,k;
while(~scanf("%d%d",&n,&k))
{
//PS:用scanf和printf要快得多,不然超时
for(int i=1;i<=n;i++)
{
scanf("%s%d",child[i].name,&child[i].num);
}
build(1,n,1);
int P=0;
//找出<=n的最大反素数
for(int i=0;RPrime[i] <= n;i++)
{
P = i;
}
int low=1;
int high = RPrime[P];
lastpos = 0;
child[lastpos].num = 0;
/*
我们已经知道<=n的最大反素数high
现在我们就是要模拟到底谁是第high个出去的
每次出去一个
用线段树来保存这个区间还有多少人
*/
while(low++<=high)
{
int mod = sum[1];
//下面这两个k的循环公式,是自己在草稿纸上演算一下就可以推出来
if(child[lastpos].num > 0)
k = ((k+child[lastpos].num-2)%mod+mod)%mod + 1;
else
k = ((k+child[lastpos].num-1)%mod+mod)%mod + 1;
update(k,1,n,1);
}
printf("%s %d",child[lastpos].name,fact[P]);
}
}