列表

详情


NC18388. 游戏

描述

小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

原站题解

上次编辑到这里,代码来自缓存 点击恢复默认模板

C++14(g++5.4) 解法, 执行用时: 181ms, 内存消耗: 12624K, 提交时间: 2018-08-31 19:08:45

#include<bits/stdc++.h>
using namespace std;
int sg[100100],vis[100100],pre[100100],suf[100100],a[100100];
vector<int>G[100100]; 
int main(){
	sg[0]=0;
	int M=1e5;
	for(int i=1;i<=M;++i)
		for(int j=i;j<=M;j+=i)
			G[j].push_back(i);
	for(int i=1;i<=M;++i){
		for(auto c:G[i])vis[sg[i-c]]=i;
		for(sg[i]=0;vis[sg[i]]==i;sg[i]++);
	}
	int n,ans=0;
	scanf("%d",&n);
	for(int i=1;i<=n;++i)scanf("%d",&a[i]);
	for(int i=1;i<=n;++i)pre[i]=pre[i-1]^sg[a[i]];
	for(int i=n;i>=1;--i)suf[i]=suf[i+1]^sg[a[i]];
	for(int i=1;i<=n;++i){
		int xr=pre[i-1]^suf[i+1];
		for(auto c:G[a[i]])if(xr==sg[a[i]-c])ans++;
	}
	printf("%d",ans);
}

C++11(clang++ 3.9) 解法, 执行用时: 146ms, 内存消耗: 11584K, 提交时间: 2018-08-31 19:22:47

#include<bits/stdc++.h>
using namespace std;
#define MAXN 100005
int n,m,i,j,s[MAXN],a[MAXN],ans;
vector<int> d[MAXN];
bool b[MAXN];
int main()
{
	n=100000;
	for(i=1;i<=n;i++)for(j=1;i*j<=n;j++)d[i*j].push_back(i);
	for(i=1;i<=n;i++)
	{
		m=d[i].size();
		for(j=0;j<m;j++)b[s[i-d[i][j]]]=1;
		for(s[i]=0;;s[i]++)if(!b[s[i]])break;
		for(j=0;j<m;j++)b[s[i-d[i][j]]]=0;
	}
	scanf("%d",&n);
	for(i=1,m=0;i<=n;i++)
	{
		scanf("%d",a+i);
		m^=s[a[i]];
	}
	for(i=1;i<=n;i++)for(j=0;j<d[a[i]].size();j++)if(s[a[i]-d[a[i]][j]]==(m^s[a[i]]))ans++;
	cout<<ans<<endl;
	return 0;
}

上一题