zoukankan      html  css  js  c++  java
  • AGC010

    原题链接

    题意简述

    给出一个n(n105)个数的序列a,足够聪明的AB两人轮流进行以下操作:
    令一个大于1的数减1,然后所有数除以gcd{a}
    如果一个人不能操作了,那么他就输了。
    输入保证所有数都是正整数并且gcd{a}=1

    分析

    这是一道和奇偶性有关的题目。
    很容易知道拿到1,1,1,...,1就输了,此时手里数的和sum等于n

    考虑sum奇偶性的转换关系。
    这里写图片描述

    或者再展开一点:
    这里写图片描述

    偶-奇必然的很好理解,重点考虑一下sum为奇数的情形。
    奇(-偶)-奇 要求gcd为偶数,因为偶/奇=偶。因此原数列%2必然是000…01的形式,而我可以将其变为000…11从而形成奇-偶 。所以奇-奇一定条件下可选的,奇-偶任何条件下可行的。

    由此再考虑n的奇偶性对答案的影响。

    1. n为偶数
      能保持sum为奇数的一方一定不会输。既然sum一直是奇数,那么就一定不会得到1,1,1,...,1的状态,必胜。而因为拿到奇数的一方一定可以给对手一个偶数,而对手只能无可奈何地还你一个奇数。所以初始sum为奇数则先手必胜,否则必败。
      时间复杂度为O(n)
    2. n为奇数
      能保持sum为偶数的一方一定不会输。但是拿到偶数的一方需要保证对手不会还回来一个奇数,下面证明这一点一定可以做到。

      证明

      首先奇数方要是想还给对手一个奇数必然要使gcd不为1,所以原数列%gcd必然是000…01的形式。再考虑这个状态是怎么达到的:
      这里写图片描述
      对于000…11,000…02,000…1k,000…0(k+1)这四种状态另一方都有办法规避000…01的结果。所以拿到偶数的一方一定能保证下一轮自己还是偶数。

      因此初始sum为偶数的话先手必胜。
      时间复杂度为O(n)

      但是初始sum是奇数并不意味着必败;因为此时还没有另一方的干扰,是有可能给对手一个奇数的。但是由于你可能只有极少的选择方案,这给了对手可乘之机:对手也有可能还回来一个奇数。以此循环往复,无法给对手奇数的一方会输掉游戏。
      因为每次都会给所有数除以一个大于1的gcd,所以最多往复log2(min{a})次,其中每次操作的复杂度是O(n)。时间复杂度最大为O(nlog2(min{a}))

    总时间复杂度最大为O(nlog2(min{a}))

    实现

    只有nsum均为奇数时无法通过判断nsum的奇偶性来得出答案。
    计算出前缀gcdg1和后缀gcdg2,然后计算gcd{g1[i1],a[i]1,g2[i+1]},如果 (sum1)/gcd 为奇数就令所有数除以gcd,然后轮到对手。若没有可能的gcd,GG。

    代码

    //Decrementing
    #include <cstdio>
    typedef long long lint;
    int const N=1e5+10;
    int n,a[N];
    int gcd(int x,int y)
    {
        if(x==-1 || y==-1) return -x*y;
        if(x==0) return y;
        else return gcd(y%x,x);
    }
    int g1[N],g2[N];
    int main()
    {
        scanf("%d",&n);
        lint sum=0;
        for(int i=1;i<=n;i++) scanf("%d",&a[i]),sum+=a[i];
        if(n%2==0)
        {
            if(sum%2==1) printf("First");
            else printf("Second");
            return 0;
        }
        if(sum%2==0) {printf("First"); return 0;}
        int cur=0;
        while(true)
        {
            bool flag=false;
            sum=0;
            for(int i=1;i<=n;i++) sum+=a[i];
            g1[0]=-1; g2[n+1]=-1;
            for(int i=2;i<=n;i++) g1[i]=gcd(g1[i-1],a[i]);
            for(int i=n-1;i>=1;i--) g2[i]=gcd(g2[i+1],a[i]);
            int g;
            for(int i=1;i<=n&&!flag;i++)
            {
                if(a[i]==1) continue;
                g=gcd(gcd(g1[i-1],g2[i+1]),a[i]-1);
                if(((sum-1)/g)%2==1) flag=true;
            }
            if(flag)
                for(int i=1;i<=n;i++) a[i]/=g;
            else
            { 
                if(cur==0) printf("Second");
                else printf("First");
                return 0; 
            } 
            cur^=1;
        }
        return 0;
    }

    注意

    挺好写的,主要是思维难度高

  • 相关阅读:
    波段是金牢记六大诀窍
    zk kafka mariadb scala flink integration
    Oracle 体系结构详解
    图解 Database Buffer Cache 内部原理(二)
    SQL Server 字符集介绍及修改方法演示
    SQL Server 2012 备份与还原详解
    SQL Server 2012 查询数据库中所有表的名称和行数
    SQL Server 2012 查询数据库中表格主键信息
    SQL Server 2012 查询数据库中所有表的索引信息
    图解 Database Buffer Cache 内部原理(一)
  • 原文地址:https://www.cnblogs.com/VisJiao/p/8485771.html
Copyright © 2011-2022 走看看