Nim定理的理解

对于博弈中的尼姆定理,是一个很由意思的博弈,当然操作范围也很大

由n堆若干个的石子,两个人轮流从某一堆取任意多的物品,规定每次至少取一个,多者不限,最后取光者得胜。
这里面有一个很核心的思路就是一种对称的操作,假设有两堆的情况下,一堆为a个石子,令一个也是a个石子,甲先对任何一个取了m个石子,只要乙乖巧的做一只复读机也取m个,那么甲必败。
那么当有n堆的情况呢
我也不知道(被打)(;´д`)ゞ
首先一步步地来,还是两堆,但是现在第一堆石子个数为a,第二堆的石子个数为b,且有a≠b
那么甲先手,只要取大的那c个,让它等于小的那一堆的数量即可,最后一定甲胜
然后考虑3个且随意取值的的情况,a,b,c
了解一下一下异或,a^b ^c=0则是先手必败。

不过我现在并不太会= =,以后会补一下看看,现在先看看另外一个东西,关于石子的抽象化处理,我们可以对石子进行一些限制,而这些限制下的取值又会发生什么了,最为普遍的公式就是SG函数的异或,这个也可以解决大部分模板问题了
2020 CCPC 1005 lunch
抽象出题目的意思,给你n堆石头,每一堆的石头有a[i],你可以把它分成a[i]/k份,当每一堆都是1时就输了;
对于一个奇数的一堆,根据唯一分解定理:任一大于1的自然数,要么本身是质数,要么可以分解为几个质数之积,且这种分解是唯一的。我们可以把这个数分成几个最小质因素相乘,而这些质因子就是石头的块数。
而对于偶数的一堆,首先知道对于两个相等的一堆,他们就是0,没有意义,8和4是等价的,也就是说对于2,2的任何次方都价于1,因此对于偶数的石头数就是取其奇质因数+1
好了完事

#include <iostream>
#include <algorithm>
#include <cstring>
#include <string>
#include <cstdio>
#include <vector>
#include<cmath>
using namespace std;

#define RG register int
#define ll long long
const int maxn=3e4+2000;
int prime[maxn+5];
int visit[maxn+5];
void Prime(){
    memset(visit,0,sizeof(visit));
    memset(prime, 0,sizeof(prime));
    for (int i = 2;i <= maxn; i++) {
       // cout<<" i = "<<i<<endl;
        if (!visit[i]) {
            prime[++prime[0]] = i;      //纪录素数, 这个prime[0] 相当于 cnt,用来计数
        }
        for (int j = 1; j <=prime[0] && i*prime[j] <= maxn; j++) {
            //cout<<"  j = "<<j<<" prime["<<j<<"]"<<" = "<<prime[j]<<" i*prime[j] = "<<i*prime[j]<<endl;
            visit[i*prime[j]] = 1;
            if (i % prime[j] == 0) {
                break;
            }
        }
    }
}
template <typename T>
inline void read(T &x){
    x=0;
    char c=0;
    T w=0;
    while(!isdigit(c))w|=c=='-',c=getchar();
    while(isdigit(c)) x=(x<<1)+(x<<3)+(c^48),c=getchar();
    if(w) x=-x;
}
int main(){
    Prime();
    int T,n,f[15],ans;
    int a[15];
    read(T);
    while(T--){
        memset(f,0,sizeof(f));
        read(n);
        for(int i=1;i<=n;i++)
        read(a[i]);
        for(int i=1;i<=n;i++)
        if(a[i]%2==0){
            while(!(a[i]%2)){
                a[i]/=2;
            }
            f[i]++;
        }
     for(int k=1;k<=n;k++){
        for(int i=2;prime[i]<=a[k];i++){//寻找奇质因数的个数
        if(prime[i]==0){
                    f[k]++;
                break;}
         while(!(a[k]%prime[i])) //
            {    
                f[k]++;
                a[k]/=prime[i];
            }
            
        }
     }
        ans=f[1];
        for(int i=2;i<=n;i++)
        ans^=f[i];
        if(ans)printf("W\n");
        else printf("L\n");
    }

    return 0;
}

注意事项:PS老白痴了,中间RE了N次,在求质因数的时候忘记考虑了大于32000的大素数,RE了5次。。。
PS麻木了,TLE,不过好歹知道int可以存1e10,建议全部int
AC了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值