• 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;
    }
    
  • 相关阅读:
    php数组操作
    DedeCMS栏目页调用当前栏目名和上级栏目名
    ThinkPHP递归删除栏目
    WebUploader
    js中的事件委托或是事件代理
    thinkphp框架if标签条件表达式
    sublime插件安装
    thinkphp分页
    织梦自定义变量使用
    php foreach用法和实例
  • 原文地址:https://www.cnblogs.com/nanke/p/2179594.html
Copyright © 2020-2023  润新知