zoukankan      html  css  js  c++  java
  • 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
    ",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;
    }
    
  • 相关阅读:
    [Clojure] 包管理器leiningen配置国内镜像仓库
    [Haskell] 为什么列表操作++很昂贵?
    js判断除了空格换行之外是否为空
    iOS上架之隐私信息访问权限(uni-app)
    vue之动态绑定class
    this
    uni-app 上传图片之压缩图片上传
    uniapp无痛刷新token
    jQuery 发送跨域请求(jsonp)
    Document
  • 原文地址:https://www.cnblogs.com/BakaCirno/p/13740401.html
Copyright © 2011-2022 走看看