链接: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
明明是个裸的板子题自己却不会搞,明明多校见过很多次因子表的打法,死活打不出来1e5的因子表。
//因子表打法
for(int i=1;i<=n;i++)
for(int j=1;j*i<=n;j++)
{
q[i*j].push_back(i);
}
这次一定要记住这三行代码。
然后就可以打出来1到100000的sg函数表了。sg函数不了解的可以戳 https://mp.csdn.net/postedit/81512877
会了SG函数后poj2975这种题你一定会了。这题就和poj2975很像了。我们每次减去一个数的因子,然后判断下剩下的值的异或值是不是0即可。先手取因子后,如果剩下的数的SG值为0的话,那么后手必败。还有一点要注意异或运算符优先级低,记得加括号。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define N 100001
int f[N],sg[N],has[N];
vector<int> q[100005];
void getSG(int n)
{
for(int i=1;i<=n;i++)
for(int j=1;j*i<=n;j++)
{
q[i*j].push_back(i);
}
memset(sg,0,sizeof(sg));
for(int i=1;i<=n;i++)
{
int len=q[i].size();
for(int j=0;j<len;j++)
has[sg[i-q[i][j]]]=1;
for(int j=0;j<=n;j++)
{
if(has[j]==0)
{
sg[i]=j;
break;
}
}
for(int j=0;j<len;j++)
has[sg[i-q[i][j]]]=0;
}
}
int a[100005];
int main()
{
int n;
getSG(100000);
scanf("%d",&n);
int ans=0;
for(int i=1;i<=n;i++)
{
int x;
cin>>x;a[i]=x;
ans^=sg[x];
}
int sum=0;
for(int i=1;i<=n;i++)
{
for(int j=0;j<q[a[i]].size();j++)
if((sg[a[i]-q[a[i]][j]]^(ans^sg[a[i]]))==0) sum++;
}
printf("%d\n",sum);
return 0;
}