• 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;
    }

    注意

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

  • 相关阅读:
    Java虚拟机一览表
    Java程序员的10道XML面试题
    bzoj 1644: [Usaco2007 Oct]Obstacle Course 障碍训练课【spfa】
    bzoj 1703: [Usaco2007 Mar]Ranking the Cows 奶牛排名【bitset+Floyd传递闭包】
    bzoj 1664: [Usaco2006 Open]County Fair Events 参加节日庆祝【dp+树状数组】
    bzoj 2100: [Usaco2010 Dec]Apple Delivery【spfa】
    bzoj 2015: [Usaco2010 Feb]Chocolate Giving【spfa】
    bzoj 1741: [Usaco2005 nov]Asteroids 穿越小行星群【最大点覆盖】
    bzoj 1645: [Usaco2007 Open]City Horizon 城市地平线【线段树+hash】
    bzoj 2060: [Usaco2010 Nov]Visiting Cows 拜访奶牛【树形dp】
  • 原文地址:https://www.cnblogs.com/VisJiao/p/8485771.html
Copyright © 2020-2023  润新知