zoukankan      html  css  js  c++  java
  • ACdream 1112 Alice and Bob (sg函数的变形+素数筛)

    题意:有N个数,Alice 和 Bob 轮流对这些数进行操作,若一个数 n=a*b且a>1,b>1,可以将该数变成 a 和 b 两个数;

             或者可以减少为a或b,Alice先,问谁能赢

    思路:首先单看对每个数进行除法的操作,我们可以知道其实是在除以每个数的素因子或素因子之间的积

             比如 70=2*5*7 我们可以变成 10(2*5)或 14(2*7) 或 35(5*7)或 2 或 5 或 7 或 1 这七种状态

             当我们把他们(2,5,7)当作3个石子也就是一堆时,然而实际上我们是将这堆石子进行nim游戏

             我拿走一个石子 =》 10(2*5) 我拿走了石子7 

                                       14 (2*7) 我拿走了石子5

                                       35 (5*7) 我拿走了石子2

             我拿走两个石子 =》 2    我拿走了石子5 和 石子7 

                                        5    我拿走了石子2 和 石子7 

                                        7    我拿走了石子2 和 石子5

             我拿走三个石子 =》 1     我拿走了石子2 和 石子5 和 石子7

             接下来我们分析把一个数n=a*b变成 a 和 b ,其实这里上面的思想很像,把它当作石子的分堆

             我可以分成             第一种 10(2*5) 和 7

                                        第二种 14(2*7) 和 5

                                        第三种 35(5*7) 和 2

             综上所诉,根据正整数唯一分解定理,任何一个正整数x必然有x=(p1^r1)*(p2^r2)*......*(pn^rn)

             定义sum=r1+r2+...+rn,这个sum的值就是这堆石子的总数,那么sg=sg[sum1]^sg[susm2]^....

             问题又来了? 这个sum我们应该如何求呢?

             我们可以通过素数筛得到每一个数的最小质因子,我们得到一个类似于递推的公式

             一个正整数的质因子的个数=(这个正整数 / 这个数的最小质因子 所得数) 的质因子个数 + 1(也就是加上这是最小质因子的数量 1)

             接下来代码实现就可以了

    代码:

    #include <iostream>
    #include <cstring>
    #include <cstdio>
    #define maxn 5000000
    using namespace std;
    
    int prime[maxn+5],k=0,samll[maxn],sum[maxn];
    bool visit[maxn+5];
    int sg[105];
    
    void get_prime()
    {
        memset(visit,false,sizeof(visit));
        memset(samll,0,sizeof(samll));
        memset(sum,0,sizeof(sum));
        for(int i=2;i<=maxn;i++)
        {
            if(visit[i]==false)
            {
                prime[k++]=i;
                for(int j=i+i;j<=maxn;j+=i)
                {
                    visit[j]=true;
                    if(samll[j]==0) samll[j]=i;
                }
                samll[i]=i;
            }
        }
        for(int i=2;i<=maxn;i++)
        sum[i]=sum[i/samll[i]]+1;
    }
    
    int get(int n)
    {
        if(sg[n]!=-1) return sg[n];
        bool vis[105];
        memset(vis,false,sizeof(vis));
        for(int i=1;i<=n;i++)
        vis[get(n-i)]=true;
        for(int i=1;i<=n/2;i++)
          vis[get(i)^get(n-i)]=true;
        int k;
        for(int i=0;i<105;i++)
        {
            if(vis[i]==false)
            {
               return sg[n]=i;
            }
        }
    }
    
    int main()
    {
       get_prime();
       memset(sg,-1,sizeof(sg));
       sg[0]=0;
       sg[1]=1;
       for(int i=2;i<=100;i++)
       {
           get(i);
       }
       int n;
       while(cin>>n)
       {
           int ans=0;
           for(int i=1;i<=n;i++)
           {
               int x;
               scanf("%d",&x);
               ans=ans^sg[sum[x]];
           }
           if(ans)
           cout<<"Alice"<<endl;
           else
           cout<<"Bob"<<endl;
       }
       return 0;
    }
  • 相关阅读:
    2015-01-27-从实验出发理解buffer与cache区别-吴伟顺
    【实习记】2014-09-26恢复linux下误删的ntfs盘中的文件
    【实习记】2014-09-24万事达卡bin查询项目总结
    【实习记】2014-09-04浏览代码查middle资料+总结我折腾过的源码浏览器
    【实习记】2014-09-01从复杂到简单:一行命令区间查重+长整型在awk中的bug
    【实习记】2014-08-29算法学习Boyer-Moore和最长公共子串(LCS)
    【实习记】2014-08-28知值求范围问题
    【实习记】2014-08-27堆排序理解总结+使用typedef指代函数指针
    【实习记】2014-08-26都是回车惹的祸——shell脚本必须是unix行尾
    【实习记】2014-08-24实习生无法映射磁盘替代方案rsync+非默认端口22设置
  • 原文地址:https://www.cnblogs.com/simplekinght/p/6180640.html
Copyright © 2011-2022 走看看