Wannafly挑战赛23 (SG函数)

链接: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;
}
发布了155 篇原创文章 · 获赞 32 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/qq_37632935/article/details/82262801
今日推荐