• 线段树单点更新+反素数 poj2886Who Gets the Most Candies


     
    题目链接

    http://poj.org/problem?id=2886

     

    题目意思:

    编号为1-n的n个人逆时针围成一圈玩游戏,每个人有一个非零的数的卡片,开始从第k个人开始,一次出圈,当第i个人出圈时,如果他的卡片上的数正数p,则他左边的第p个人下个出圈,如果他卡片上的数是负数p,则他右边的第p个人下个出圈。当第i个人出圈时,他获奖励是F(i),F(i)为i的正约数的个数。求获得的最大的奖励是哪个人,及奖励数。

     

    解题思路:

    1、反素数的应用。设F(i)为i的正约数个数,若对任意的x<i,有F(x)<F(i),则i为反素数。找出不超过n的第一个最大的反素数即可。

    打印反素数代码:

    #include<iostream>
    using namespace std;
    __int64 bestnum;
    __int64 bestsum;
    __int64 n;
    __int64 prime[10]={2,3,5,7,11,13,17,19,23};
    __int64 limit[10];
    void creatLimit(__int64 n)
    {
    memset(limit,0,sizeof(limit));
    __int64 i,rn;
    for(i=0;i<=8;i++)
    {
       rn=n;
       while(rn>prime[i])
       {
        limit[i]++;
        rn/=prime[i];
       }
    }
    }
    void creatRPrime(__int64 num,__int64 k,__int64 sum,__int64 limit) //num:数 sum: 因子数
    {
    __int64 i,p;
    if(num>n)return;
    if(sum>bestsum)
    {
       bestsum=sum;
       bestnum=num;
    }
    else
    {
       if(sum==bestsum && num<bestnum)
        bestnum=num;
    }
    if(k>8)
       return;
    p=1;
    for(i=1;i<=limit;i++)
    {
       p*=prime[k];
       creatRPrime(num*p,k+1,sum*(i+1),i);
    }
    }
    __int64 log2(__int64 n)   //求大于等于log2(n)的最小整数
    {
    __int64 i = 0;
    __int64 p = 1;
    while(p<n)
    {
       p*=2;
       i++;
    }
    return i;
    }
    int main()
    {
       freopen("out.txt","w",stdout);
       __int64 save1[110],save2[110];
       save1[1]=1,save2[1]=1;
       int last=1;
    
       int len=1;
    
       for(int i=2;i<=600000;i++)
       {
           n=i;
    
           bestsum=0;
           bestnum=n+1;
           creatLimit(n);
           creatRPrime(1,0,1,log2(n));//bestnum:存的是<=n的最大反素数
           //printf("%I64d %I64d\n",bestnum,bestsum);
           if(bestnum!=last)
           {
              // printf("%I64d ",bestnum,bestsum);
              save1[++len]=bestnum,save2[len]=bestsum;
               last=bestnum;
           }
    
       }
       printf("%I64d",save1[1]);
       for(int i=2;i<=len;i++)
         printf(",%I64d",save1[i]);
    
        putchar('\n');
        printf("%I64d",save2[1]);
       for(int i=2;i<=len;i++)
            printf(",%I64d",save2[i]);
        putchar('\n');
    
    
    return 0;
    }
    
    

    2、用一颗线段树维护原始区间内剩下的人的个数,从而找到顺序出圈的人的原始序号。注意借用前面的人在上一区间的标号位置,这点很关键。

     关键:上一原始位置与下一顺序位置的关系。

    详见代码:

    #include<iostream>
    #include<cmath>
    #include<cstdio>
    #include<cstdlib>
    #include<string>
    #include<cstring>
    #include<algorithm>
    #include<vector>
    #include<map>
    #include<stack>
    #include<list>
    #include<queue>
    #define eps 1e-6
    #define INF (1<<30)
    #define PI acos(-1.0)
    using namespace std;
    
    #define lson l,m,rt<<1
    #define rson m+1,r,rt<<1|1
    #define maxn 510000
    int sum[maxn*4];
    
    /*
    freopen("data.in","r",stdin);
    freopen("data.out","w",stdout);
    */
    
    struct Inf
    {
        char name[30];
        int value;
    }inf[maxn];
    
    void build(int l,int r,int rt)
    {
        sum[rt]=r-l+1;
    
        if(l==r)
            return ;
    
        int m=(l+r)>>1;
        build(lson);
        build(rson);
        return ;
    
    }
    
    int update(int target,int l,int r,int rt)
    {
        sum[rt]--;
    
        if(l==r)
            return l;
    
        int m=(l+r)>>1;
    
        if(target<=sum[rt<<1])
            return update(target,lson);
        return update(target-sum[rt<<1],rson);
    }
    
    int main()
    {
        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};
    
        int factors[]={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}; //一共36个
    
        int n,k,&mm=sum[1]; //用引用,当人数改变时,mm也改变
    
        while(scanf("%d%d",&n,&k)!=EOF)
        {
            //getchar();
            build(1,n,1);
            for(int i=1;i<=n;i++)
                scanf("%s%d",inf[i].name,&inf[i].value);
    
            int cnt=0;
            while(antiprime[cnt]<=n)  //找到不超过n的最大的反素数
                cnt++;
            cnt--;
    
            int pos=0;
            inf[pos].value=0;  //构建第一个人的情况
            for(int i=1;i<=antiprime[cnt];i++) //一步一步找出顺序出圈的人,直到第antiprime[cnt]个人为止
            {
                if(inf[pos].value>0)
                    k=((k+inf[pos].value-2)%mm+mm)%mm+1; //这样可以取到第m个数,不然用-的话为零
                else
                    k=((k+inf[pos].value-1)%mm+mm)%mm+1;
    
                pos=update(k,1,n,1);
            }
           // printf("pos:%d\n",pos);
            printf("%s %d\n",inf[pos].name,factors[cnt]);
    
        }
    
    
        return 0;
    }
    


     

     

  • 相关阅读:
    再谈iframe自适应高度
    一个软件外包老鸟对外包业的反思
    需求分析中减少客户摩擦的若干法则
    C# 进制转换
    'filter' is not a known css property name
    Ajax之ModalPopupExtender 的后台调用
    Microsoft SQL Server 中的小数数据类型
    ExtJS Combobox 如何设置默认和取值问题
    wp7 控制控件显隐
    wp7 MediaElement播放
  • 原文地址:https://www.cnblogs.com/jiangu66/p/3000340.html
Copyright © 2020-2023  润新知