[AGC010D] Decrementing

星河倒注發表於2024-10-21

首先考慮最簡單的情況,如果有一個數是 \(1\),那麼第二步沒有作用,勝負是固定的,先判掉。

然後發現題目給了一個很奇怪的條件:所有數的最大公約數為 \(1\),也就是至少有一個奇數,這提示我們從奇偶數下手。

發現第二步中的最大公約數的奇數因子是毫無意義的,因為無論是奇數還是偶數除以一個奇數,奇偶性都不變,沒有改變第一步操作次數的奇偶性。

這給了先手一種必勝的思路,如果只進行第一步,先手必勝,先手又能保證兩人每一次第二步中的最大公約數為奇數就必勝。

先手只需要每一次隨便選一個偶數減 \(1\),由於初始至少有一個奇數,所以後手操作的時候至少有兩個奇數,最大公約數一定為奇數,先手的操作最大公約數就更顯然是奇數了。

這種情況的初始條件是隻進行第一步先手必勝,也就是偶數個數為奇數。

如果只進行第一步先手必敗呢?這時候先手肯定不能留給後手奇數,否則後手採用前面的方法必勝。所以先手只有一種選擇,第一步刪奇數。

如果初始奇數的個數大於 \(1\),那麼先手刪不完所有的奇數,後手必勝。

到現在,我們有三種判定了:

  • 如果有 \(1\) 直接判定
  • 如果有奇數個偶數先手必勝
  • 如果有偶數個偶數並且有大於一個奇數後手必勝

如果恰好有偶數個偶數而且只有一個奇數,先手會刪那個奇數,直接模擬這一次操作將奇數減 \(1\),這時所有的數都為偶數,最小公因數最小為 \(2\),最多進行 \(log\) 次就會變成上面三種情況的一種。

單次模擬時間複雜度為 \(O(nloga)\),總時間複雜度為 \(O(nlog^{2}a)\)

#include<bits/stdc++.h>
using namespace std;
const int N=100010;
int n;
int a[N];
int ji,ou,now=1,sum=0;
bool flag=false;

void change(){
    for(int i=1;i<=n;i++){
        if(a[i]&1) a[i]--;
    }
    int gcd=a[1];
    for(int i=2;i<=n;i++) gcd=__gcd(gcd,a[i]);
    for(int i=1;i<=n;i++) a[i]/=gcd;
}

int main(){
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    cin>>n;
    for(int i=1;i<=n;i++){
        cin>>a[i];
        if(a[i]&1) ji++;
        else ou++;
        if(a[i]==1) flag=true;
        sum+=(a[i]-1);
    }
    if(flag){
        if(sum&1) cout<<"First";
        else cout<<"Second";
        return 0;
    }
    if(ou&1){
        cout<<"First";
        return 0;
    }
    else if(!(ou&1) && ji>1){
        cout<<"Second";
        return 0;
    }
    while(1){
        now=3-now;
        change();
        ji=ou=0;
        sum=0;
        for(int i=1;i<=n;i++){
            if(a[i]&1) ji++;
            else ou++;
            if(a[i]==1) flag=true;
            sum+=(a[i]-1);
        }
        if(flag){
            if(!(sum&1)) now=3-now;
            break;
        }
        if(ou&1){
            break;
        }
        else if(!(ou&1) && ji>1){
            now=3-now;
            break;
        }
    }
    if(now==1) cout<<"First";
    else cout<<"Second";
    return 0;
}