【BZOJ3576】江南乐(博弈论)

【BZOJ3576】江南乐(博弈论)

题面

BZOJ
洛谷

题解

无论一堆石头怎么拆分,都并不能改变它是一个(Multi-SG)的事实。
既然每一组的(F)都是固定的,那么我们预处理所有的可能的堆,而将石子拆分成若干堆,也只需要考虑(SG)函数的值就好了。
但是这样子求(SG)值的复杂度是(O(V^2))的,其中(V)是值域,也就是(10^5)
再分析一下,将(x)个式子拆分成的最少的石子个数是([x/m]),最多的情况是([x/m+1])
因为([x/m])可以数论分块,而对于(SG)函数值的影响之和奇偶性相关,所以只需要考虑([x/m],[x/m+1])出现次数的奇偶性一共(4)中情况考虑。
([x/m])个数的奇偶性只和(m)相关,([x/m+1])个数的奇偶性只和(x\%m)相关,而(x\%m=x-m[x/m])
([x/m])在数论分块中是定值,不需要额外考虑,那么只需要考虑(m)的奇偶性就好啦。
然而预处理的话复杂度比较爆炸,毕竟我们不一定所有的值都会被用到,那么就写一个记忆化搜索。

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
#define MAX 100100
inline int read()
{
	int x=0;bool t=false;char ch=getchar();
	while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
	if(ch=='-')t=true,ch=getchar();
	while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
	return t?-x:x;
}
int SG[MAX],vis[MAX];
int getSG(int x)
{
	if(~SG[x])return SG[x];
	for(int i=2,k;i<=x;i=k+1)
	{
		k=x/(x/i);
		for(int j=i;j<=min(i+1,k);++j)
		{
			int s=0;
			if((x%j)&1)s^=getSG(x/j+1);
			if((j-x%j)&1)s^=getSG(x/j);
			vis[s]=x;
		}
	}
	for(int i=0;;++i)if(vis[i]!=x)return SG[x]=i;
}
int main()
{
	int T=read(),F=read();
	memset(SG,-1,sizeof(SG));
	for(int i=0;i<F;++i)SG[i]=0;
	while(T--)
	{
		int n=read(),s=0;
		while(n--)s^=getSG(read());
		printf("%d ",(bool)s);
	}
	return 0;
}

原文地址:https://www.cnblogs.com/cjyyb/p/9494360.html