pku2886 Who Gets the Most Candies?(线段树+反素数打表)

pku2886 Who Gets the Most Candies?

题意:
    有一排编号为1~N的小孩顺时针围成圈,每人手上有一张编号为a[i]的卡片,游戏从第K个小孩开始,他亮出自己的卡片数字,若a[i]大于0,那么左数第a[i]个小孩出圈,否则右数第a[i]个小孩出圈,游戏一直进行,知道所有孩子都出圈,第p个出圈的将会得到f(p)个糖果,f(p)表示p的因子数,问谁拿到的最多的糖果。

分析:其实我是先知道 了用线段树做,才拼命想该用线段树怎么做的。

思路大致是这样的:每次都默认是从剩下的第一个开始数,顺时针让第count个人出圈。所以必须在每一个人出圈时,算出他左边有多少,再通过他手上的卡片值a[i]算出接下来应该让哪一个人出圈;

至于函数f(p) 就是先打表,算出反素数值,还有算出起因数的个数,之后只需求出最大的f(p),(p<=N),然后算出第p个出圈的人是谁即可。

反素数打表:http://hi.baidu.com/speakless/blog/item/1be8d61b63711cd5ac6e751f.html

用线段树实现过程中有俩个难点:

1):求出每一个刚出圈的人的剩下的左边还有多少人,其实这个可以在算出每一个出圈的人时,顺便计算他左边的人数,看代码就清楚了;

2):让顺时针第count个人出圈之后,应该怎样维护线段树,在线段树中添加一个域num,表示该区间还剩下的人数,所以每出去一个人,对应区间就减一;

#include<iostream>
#include<math.h>
#define MAXN 500010
using namespace std;
const int antiprime[] = {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, 665280};
const int factorNum[] = {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, 224};
struct node
{
	int num;
}p[MAXN*4];
struct name
{
	char nm[11];
}na[MAXN];
int a[MAXN];
int n,count,deq;
void bulid(int k,int s,int t)
{
	if(s==t)
	{
		p[k].num=1;
		return ;
	}
	int kl=k<<1,kr=kl+1,mid=(s+t)>>1;
	bulid(kl,s,mid);
	bulid(kr,mid+1,t);
	p[k].num=p[kr].num+p[kl].num;
}
void dequeue(int k,int ln,int q,int s,int t)
//ln表示左边有多少个数,q表示要删除该区间内的第q个人,[s,t]表示当前区间
{
	if(s==t)
	{
		p[k].num=0;
		count=ln-1;
		deq=s;
		return ;
	}
	int kl=k<<1,kr=kl+1,mid=(s+t)>>1;
	if(p[kl].num>=q)//若左儿子人数不足q个,则要删除的对象在右儿子上,但要注意q的值要相应的减去左儿子的人数
		//计算左边人数的时候也类似,若要删除的对象在左儿子上,则在删除对象的左边人数肯定减去右儿子的人数,因为一开始ln表示的是总人数
		dequeue(kl,ln-p[kr].num,q,s,mid);
	else dequeue(kr,ln,q-p[kl].num,mid+1,t);
	p[k].num=p[kl].num+p[kr].num;
}
int main()
{
	while(scanf("%d %d",&n,&deq)==2)
	{
		for(int i=1;i<=n;i++)
			scanf("%s %d",na[i].nm,&a[i]);
		bulid(1,1,n);
		int tag=0;
        while(antiprime[tag]<=n)//返回antiprime中小于等于n的最大反素数下标tag
            tag++;
        tag--;
		int cas=antiprime[tag];//第ith个退出的人得到最多的糖;
		count=deq;
		while(cas--)
		{
			dequeue(1,p[1].num,count,1,n);
			if(cas==0)//这步不能省的呀,因为后面求余时,p[1].num可能等于0
				break;
			if(a[deq]>0)
			{
				count=(count+a[deq]+p[1].num)%p[1].num;
				if(count==0)
					count=p[1].num;
			}
			else 
			{
				count=p[1].num-count;
				count=(count-a[deq]+p[1].num)%p[1].num;
				if(count==0)
					count=1;
				else count=p[1].num-count+1;
			}
		}
		printf("%s %d\n",na[deq].nm,factorNum[tag]);
	}
	return 0;
}
posted @ 2011-09-17 13:55  枕边梦  阅读(245)  评论(0编辑  收藏  举报