链接:https://www.nowcoder.com/acm/contest/161/B
题目描述
小N和小O在玩游戏。他们面前放了n堆石子,第i堆石子一开始有ci颗石头。他们轮流从某堆石子中取石子,不能不取。最后无法操作的人就输了这个游戏。但他们觉得这样玩太无聊了,更新了一下规则。具体是这样的:对于一堆有恰好m颗石子的石头堆,假如一个人要从这堆石子中取石子,设他要取石子数为d,那么d必须是m的约数。最后还是无法操作者输。
现在小N先手。他想知道他第一步有多少种不同的必胜策略。一个策略指的是,从哪堆石子中,取走多少颗石子。只要取的那一堆不同,或取的数目不同,都算不同的策略。
输入描述:
第一行一个整数n。
接下来一行n个整数,分别代表每堆石子的石子数目。
数据保证输入的所有数字都不超过105,均大于等于1,且为整数。
输出描述:
一行一个整数代表小$N$第一步必胜策略的数量。
示例1
输入
10
47 18 9 36 10 1 13 19 29 1
输出
7
思路:
先打因数表再打sg函数表,最后求ans,具体解释见代码~
代码如下:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<vector>
#include<cmath>
#include<map>
#include<set>
#include<string>
#include<cstring>
using namespace std;
#define ll long long
const int mod=1e9+7,md=1e9+6,maxn=100005;
int sg[maxn],a[maxn],vis[maxn];
int n,cnt=0;
vector<int>f[maxn];
void get_f(){
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);
}
}
}
}
void get_sg(){
for(int i=1;i<=maxn;i++){
cnt++;
for(int j=0;j<f[i].size();j++){
vis[sg[i-f[i][j]]]=cnt;
}
for(int j=0;;j++){
if(vis[j]!=cnt){
sg[i]=j;break;
}
}
}
return ;
}
int main(){
get_f();
get_sg();
int n;
scanf("%d",&n);
int sum=0;
for(int i=0;i<n;i++){
scanf("%d",&a[i]);
sum^=sg[a[i]];
}
int ans=0;
for(int i=0;i<n;i++){
int x=sum^sg[a[i]];//相当于没有考虑子游戏i时的情况
for(int j=0;j<f[a[i]].size();j++){
int d=f[a[i]][j];
if((sg[a[i]-d])==x)ans++;//在第i堆取d个石子后的sg值==x,那么说明取了d后可以达到和sum相同的状态
}
}
printf("%d\n",ans);
}
题目连接:http://acm.hdu.edu.cn/showproblem.php?pid=1850
这题是上题的简化版~
代码如下:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<vector>
#include<cmath>
#include<set>
#include<string>
#include<cstring>
#define ll long long
using namespace std;
const int N=1000000;
int a[N+5];
int main(){
int m;
while(scanf("%d",&m)!=EOF){
if(m==0)break;
int x=0;
for(int i=1;i<=m;i++){
scanf("%d",&a[i]);
x^=a[i];
}
int res=0;
for(int i=1;i<=m;i++){
int tmp=x^a[i];
for(int j=1;j<=a[i];j++){
if((a[i]-j)==tmp)res++;
}
}
printf("%d\n",res);
}
}