1. 程式人生 > 實用技巧 >CCPC2020 網路預選賽 0005 Lunch(SG博弈+質因數分解)

CCPC2020 網路預選賽 0005 Lunch(SG博弈+質因數分解)

傳送門

題意

\(n\)個數\(a_1,a_2,...,a_n\),有兩個選手,依次操作,對於一個數\(a_i\),每個人可以選擇\(a_i\)的一個因數\(k\),將\(a_i\)分為\(k\)\(\frac{a_i}{k}\),如果一個人不能操作了,那麼他就輸了。問先手是否必贏。

題解

如果學過sg函式,那麼結果就是\(sg(a_1)\oplus sg(a_2)\oplus ...\oplus sg(a_n)\)是否為真值。那麼來考慮一下\(sg(x)\)的值,設\(d_1,d_2,...,d_k\)\(x\)的因數,那麼\(x\)可以到達的狀態就是\(d_1\)\(\frac{x}{d_1}\)

\(d_2\)\(\frac{x}{d_2}\),……,\(d_k\)\(\frac{x}{d_k}\)\(sg(x)\)的值就是這幾個狀態的異或和。對於\(d_i\)\(\frac{x}{d_i}\)這個狀態,它的\(sg\)值就是\(d_i\)\(sg(\frac{x}{d_i})\)的異或和。如果\(d_i\)是偶數,那麼這個狀態的\(sg\)值就是\(0\);否則這個狀態的\(sg\)值就是\(sg(\frac{x}{d_i})\)。根據這個結果,可以寫一個暴力求\(sg\)函式來解決這個問題的程式:

int sg(int x){
    // cout<<"x="<<x<<endl;
    if(vis[x]) return f[x];
    unordered_map<int,bool> cnt;
    for(int i=1;i*i<=x;i++){
        if(x%i!=0) continue;
        int a=i,b=x/i;
        if(b%2==0||a==1) cnt[0]=1;
        else cnt[sg(a)]=1;
        if(i==x/i||i==1) continue;
        swap(a,b);
        if(b%2==0||a==1) cnt[0]=1;
        else cnt[sg(a)]=1;
    }
    for(int i=0;;i++)
        if(!cnt[i]){
            vis[x]=1;
            return f[x]=i;
        }
}

當然這個程式是會超時的,可以打表看看每個數的\(sg\)值,可以發現:如果\(x=2^{b_0}a_1^{b_1}a_2^{b_2}...a_k^{b_k}\),那麼\(sg(x)=[b_0>0]+b_1+b_2+...+b_k\)。所以直接把這個\(sg\)函式的解法換成質因數分解就可以了。

程式碼

int n,a[11];
int prime[N],vis[N],cnt;

int sg(int x){
    vector<PII> vec;
    for(int i=1;i<=cnt&&prime[i]*prime[i]<=x;i++){
        if(x%prime[i]!=0) continue;
        vec.push_back({prime[i],0});
        while(x%prime[i]==0) vec.back().y++,x/=prime[i];
    }
    if(x>1) vec.push_back({x,1});
    int res=0;
    for(PII p:vec)
        if(p.x==2) res++;
        else res+=p.y;
    return res;
}

void Solve(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    int res=0;
    for(int i=1;i<=n;i++) res^=sg(a[i]);
    printf("%s\n",res?"W":"L");
}

int main(){
    for(int i=2;i<=1e5;i++){
        if(!vis[i]) {prime[++cnt]=i;vis[i]=1;}
        for(int j=1;j<=cnt&&prime[j]*i<=1e5;j++){
            vis[prime[j]*i]=1;
            if(i%prime[j]==0) break;
        }
    }
    int T;scanf("%d",&T);
    while(T--) Solve();
    return 0;
}