博弈——游戏(SG 函数)
题目描述
小 N 和小 O 在玩游戏。他们面前放了 n 堆石子,第 i 堆石子一开始有 ci 颗石头。他们轮流从某堆石子中取石子,不能不取。最后无法操作的人就输了这个游戏。但他们觉得这样玩太无聊了,更新了一下规则。具体是这样的:对于一堆有恰好 m 颗石子的石头堆,假如一个人要从这堆石子中取石子,设他要取石子数为 d,那么 d 必须是 m 的约数。最后还是无法操作者输。
现在小 N 先手。他想知道他第一步有多少种不同的必胜策略。一个策略指的是,从哪堆石子中,取走多少颗石子。只要取的那一堆不同,或取的数目不同,都算不同的策略。
输入描述
第一行一个整数 n。
接下来一行 n 个整数,分别代表每堆石子的石子数目。
数据保证输入的所有数字都不超过105,均大于等于 1,且为整数。
输出描述
一行一个整数代表小 N 第一步必胜策略的数量。
示例
输入
10
47 18 9 36 10 1 13 19 29 1
输出
7
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+5;
int S[maxn],SG[maxn],a[maxn],n,cnt=0;
vector<int> f[maxn];
void getSG()
{
memset(SG,0,sizeof(SG));
for(int i=1;i<=maxn;i++){
cnt++;
for(int j=0;j<f[i].size();j++){
S[SG[i-f[i][j]]]=cnt;
}
for(int t=0; ;t++){
if(S[t]!=cnt){
SG[i]=t;
break;
}
}
}
return ;
}
int main()
{
for(int i=1;i<=maxn;i++){
for(int j=1;j*j<=i;j++){
if(i%j==0){
f[i].push_back(j);
if(i/j!=j){
f[i].push_back(i/j);
}
}
}
}
getSG();
cin>>n;
int sum=0;
for(int i=1;i<=n;i++){
cin>>a[i];
sum^=SG[a[i]];
}
int ans=0;
for(int i=1;i<=n;i++){
for(int j=0;j<f[a[i]].size();j++){
int x=f[a[i]][j];
if(SG[a[i]-x]==(sum^SG[a[i]])){
ans++;
}
}
}
cout<<ans<<endl;
return 0;
}