[AGC10D]Decrementing——数学分析+博弈 大佬们的博客 Some Links

题目大意:

有一串gcd为1序列,两个人轮流操作,每一次选择一个大于1的数字减一之后将序列中的每一个数除以整个序列的gcd,最后操作不了的人输,问是否先手必胜。

思路:

显然输了的人的状态是全部都是1的,也就是 i = 1 n ( a i 1 ) 为0,是一个偶数,先手要必胜的话要想办法让自己是奇数,这样最后的偶数的状态才会让别人拿到。

  1. 如果 n 为偶数,有 n 2 ,若 i = 1 n a i 为奇数的话,那么奇数的个数 1 ,为了让对方只得到偶数,只需要使操作过的奇数个数 1 ,选择性地操作奇数或者偶数,所以先手必胜。
  2. 如果 n 为偶数,但是最开始的和为偶数的话,先手无论怎么操作一次之后就变成了后手必胜的状态,所以先手必败。
  3. 如果 n 为奇数, n = 1 可以直接得到结果,所以我们只要考虑 n 3 就好了,那么每一个人肯定是都希望拿到偶数,如果先手是拿到了偶数,从最开始的gcd=1可以知道奇数的个数 2 我们只要让对方拿到的奇数个数 2 ,发现这个条件可以满足,此时先手必胜。
  4. 最后一种情况就是 n 为奇数并且先手拿到的是奇数,如果有1存在直接失败,如果有大于等于三个的奇数也是失败,因为先手无法造成全部都是偶数的局面来留给对方奇数,所以我们只需要对只有一个奇数的情况进行两个人不断地递归就好了,如果对那个奇数进行了操作之后还是偶数或者是奇数并且可以确定对方必败的话,就可以停止了。
/*=======================
 * Author : ylsoi
 * Problem : AGC10D
 * Algorithm : game 
 * Time : 2018.7.25
 * =======================*/
#include<bits/stdc++.h>

#define REP(i,a,b) for(int i=a;i<=b;++i)
typedef long long ll;

using namespace std;

void File(){
    freopen("AGC10D.in","r",stdin);
    freopen("AGC10D.out","w",stdout);
}

const int maxn=1e5+10;
int n;
ll a[maxn],sum;

bool dfs(bool now){
    REP(i,1,n)if(a[i]%2==1)--a[i];
    ll d=a[1];
    REP(i,1,n)d=__gcd(a[i],d);
    int cnt=0; sum=0;
    bool flag=0;
    REP(i,1,n){
        a[i]/=d;
        sum+=a[i];
        if(a[i]%2==1)++cnt;
        if(a[i]==1)flag=1;
    }
    if(sum%2==0)return now^1;
    if(sum%2==1 && (cnt>=2 || flag))return now;
    return dfs(now^1);
}

int main(){
    File();
    scanf("%d",&n);
    REP(i,1,n){
        scanf("%lld",&a[i]);
        sum+=a[i];
    }
    if(n%2!=sum%2)puts("First");
    else if(n%2==0 && sum%2==0)puts("Second");
    else{
        int cnt=0;
        REP(i,1,n){
            if(a[i]%2==1)++cnt;
            if(cnt>=2 || a[i]==1){
                puts("Second");
                return 0;
            }
        }
        if(dfs(1))puts("First");
        else puts("Second");
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/ylsoi/article/details/81210097