bzoj4600 [Sdoi2016]硬币游戏 sg函数+结论

sg[maxQ][二的次数][三的次数]=前面的都是正面的胜负状态

C不一样的相互独立

首先顺序是不重要的,因为他有一个模仿关系。

比如   9,3是0    必胜策略是 选9翻9 、3

  由于是异或,所以必胜策略可以理解为 选9 翻 9  ,这样3的位置就需要翻两遍,也就相当于翻0次

就可以理解为3这位置状态已经是0(实际操作相当于可以模仿掉)

所以如果存在必胜策略,一定可以拆为每个点sg值的若干组合。选取一个0点把 前面的0点变成1的操作

可以在独立的点角度 理解为保留前面的0点,剩下的用 前面0点的sg来异或等于0,

因为若先手为0,后手一定可以为1 先手为1,后手一定为0,就相当于不能翻

也可以用异或的性质理解。。

码:

#include
#include
#include
using namespace std;
int sg[23][55][55],n,T,i,j,Q,ans,o,er[30005],san[30005];
int SG(int Q,int a,int b)
{
	if(sg[Q][a][b]!=-1)return sg[Q][a][b];
int i,j,q,p;
bool v[101];
memset(v,0,sizeof(v));
for(p=1;p<=a;p++)
{int lin=0;
for(q=1;q<=Q;q++)
{
if(p*q>a)break;	
	lin^=SG(Q,a-p*q,b);
	v[lin]=1;
}
}
for(p=1;p<=b;p++)	
{int lin=0;
for(q=1;q<=Q;q++)
{
if(p*q>b)break;	
	lin^=SG(Q,a,b-p*q);
	v[lin]=1;
}
}
	for(i=0;i<=100;i++)
	{
		if(v[i]==0)
		{sg[Q][a][b]=i;break;}
	}
	return sg[Q][a][b];
}
int main()
{
	scanf("%d",&T);
	memset(sg,-1,sizeof(sg));
	for(Q=1;Q<=20;Q++)
	for(i=0;i<=15;i++)
	for(j=0;j<=15;j++)
	{
		SG(Q,i,j);
	}
	int linn=0;
	for(i=1;i<=30000;i++)
	{
		o=i;
			int o2=0;
			int o3=0;
			while(o%2==0)
			o/=2,o2++;
			while(o%3==0)
			o/=3,o3++;
			er[i]=o2;san[i]=o3;
		linn=max(linn,max(o2,o3));
	}
	while(T--)
	{ans=0;
		scanf("%d%d",&n,&Q);
		for(i=1;i<=n;i++)
		{
			scanf("%d",&o);
			if(o==1)continue;
			ans^=sg[Q][er[i]][san[i]];
		}
		if(ans==0)printf("lose\n");
		else printf("win\n");
	}
 } 

  


你可能感兴趣的:(题目)