【AtCoder】【AGC010D】Decrementing

Description

给出一个长度为n的序列 a ,A、B两个人轮流操作:
1. 选择一个不为1的数,将其-1;
2. 把序列中的所有数/g,g为所有数的gcd;

操作不了的人输。

Solution

博弈题一堆结论…

输的人肯定是拿到111111的局面,
设sum为序列所有数的和,
要想赢,肯定要避免拿到sum=n的情况,

当n为偶数时:如果一直拿到sum为奇数的情况,那么必赢,

结论1:n为偶数且sum为奇数时,先手必胜,否则必输;

我们先来看一下sum在经历了一次操作后的奇偶变化:

(图片来源,侵删)
这里写图片描述

显然,先手可以让每一轮操作后都留给对手一个偶数,而对手只能还回来一个奇数,所以必赢;

偶数必输显然;

当n为奇数时:
结论2:n为奇数且sum为偶数时,先手必胜;
证明:先手要想赢,就必须不让对手还回来一个奇数,对手要想还回来一个奇数,就必须保证gcd不为0,
也就是说,对手在操作前,序列中所有数%gcd是这样的:0,0…,0,1
所以先手只要避免这种情况即可,那先手操作前的序列%gcd有这几种情况:
0,0,0,0,2
0,0,0,1,1
0,0,0,0,k+1
0,0,0,1,k
(后两个要除以那一局的gcd)

我们发现,这4种情况均可以破解,使之不变成0,0,0,0,1,

结论2:n为奇数且sum为奇数时,先手不一定输;
例:2,2,3,这样的话先手必赢,

先手肯定是必须给对手一个奇数,否则对手就赢了
所以就判断能否给对手一个奇数,显然每局如果可以的话方案唯一,
不行当前的人输
每次的gcd必须>1,所以是log次的。

Code

#include <cstdio>
#include <cstdlib>
#include <algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fod(i,a,b) for(int i=a;i>=b;i--)
using namespace std;
typedef long long LL;
const int N=100500;
int read(int &n)
{
    char ch=' ';int q=0,w=1;
    for(;(ch!='-')&&((ch<'0')||(ch>'9'));ch=getchar());
    if(ch=='-')w=-1,ch=getchar();
    for(;ch>='0' && ch<='9';ch=getchar())q=q*10+ch-48;n=q*w;return n;
}
int m,n,ans;
int a[N];
int g[N];
int gcd(int x,int y){return y?gcd(y,x%y):x;}
int main()
{
    int w;
    LL q;
    read(n);
    fo(i,1,n)q+=(LL)read(a[i]);
    if(n==1&&a[1]==1)return printf("Second\n"),0;
    if(n==1)return printf("First\n"),0;
    if(!(n&1))
    {
        if(q&1)printf("First\n");
        else printf("Second\n");
        return 0;
    }
    if(!(q&1))return printf("First\n"),0;
    for(bool I=0;1;I=!I)
    {
        g[n]=a[n];
        LL t=a[n]-1;
        fod(i,n-1,1)g[i]=gcd(g[i+1],a[i]),t+=(LL)a[i];
        bool OK=0;
        q=a[1];
        if(a[1]-1&&(1&(t/(ans=gcd(g[2],a[1]-1)))))OK=1,--a[1];
        else fo(i,2,n)if(a[i]-1)
        {
            w=gcd(q,a[i]-1);
            if(i<n)w=gcd(w,g[i+1]);
            if((1&(t/(ans=w))))
            {
                OK=1,--a[i];
                break;
            }
            q=gcd(q,a[i]);
        }else break;
        if(!OK)
        {
            if(I)printf("First\n");
            else printf("Second\n");
            return 0;
        }
        fo(i,1,n)a[i]/=ans;
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/howarli/article/details/79426170