[SDOI2016]硬币游戏

题目

翻硬币游戏啊

首先对于这类游戏只要满足如下的规则,就可以用一种特殊的方式解决了

我们假定只能翻正面朝上的硬币

  1. 我们可以根据某些约束条件的翻硬币(一次反掉连续几个,或者具有某些特殊性质的),但是翻掉的硬币中最右边的那个比如是从正面反到反面

  2. 谁不能翻硬币了谁输

对于满足这样的特征的硬币游戏,我们有这样一个定理:局面的(SG)函数等于局面中每一个正面朝上的硬币单独存在的局面的(SG)函数的异或和

图

去论文里偷了一张图,大概就是这个样子

对于这个题我们能翻动的是反面朝上的硬币,所以我们求一下所有反面朝上的硬币但对存在的时候的(SG)函数,求一个异或和就好了

我们发现这个题硬币翻动情况,只和这个硬币的位置(x)分解成(x=2^a imes 3^b imes c)时候的(a,b)有关系

于是我们可以设(sg[a][b])表示一枚位于(=2^a imes 3^b imes c)的硬币单独存在的(SG)函数值

于是我们按照题意模拟一下这个硬币能转移到的局面,取一个(mex)就好了

代码

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define re register
#define LL long long
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
inline int read() {
	char c=getchar();int x=0;while(c<'0'||c>'9') c=getchar();
	while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-48,c=getchar();return x;
}
int T,n,M,m;
int sg[16][15],tax[100005];
inline void Pre() {
	sg[0][0]=0;
	for(re int a=0;a<=15;a++)
		for(re int b=0;b<=10;b++) {
			if(!a&&!b) continue;++m;
			for(re int p=1;p<=a;p++)
				for(re int q=1;q<=M&&p*q<=a;q++) {
					int now=0;
					for(re int j=0;j<=q;j++)
						now^=sg[a-p*j][b];
					tax[now]=m;
				}
			for(re int p=1;p<=b;p++)
				for(re int q=1;q<=M&&p*q<=b;q++) {
					int now=0;
					for(re int j=0;j<=q;j++)
						now^=sg[a][b-p*j];
					tax[now]=m;
				}
			while(tax[sg[a][b]]==m) sg[a][b]++;
		}
}
int main() {
	T=read();
	while(T--) {
		n=read(),M=read();memset(sg,0,sizeof(sg));Pre();
		int ans=0;
		for(re int x,i=1;i<=n;i++) {
			x=read();if(x) continue;
			int a=0,b=0;int pos=i;
			while(pos%2==0) pos/=2,a++;
			while(pos%3==0) pos/=3,b++;
			ans^=sg[a][b];
		}
		puts(ans?"win":"lose");
	}
	return 0;
}
原文地址:https://www.cnblogs.com/asuldb/p/10772777.html