【BZOJ 4600】【SDOI 2016】硬币游戏

http://www.lydsy.com/JudgeOnline/problem.php?id=4600
转化成nim游戏
因为对于每一个反面朝上的硬币编号可以拆成(2^a3^bc),选择这个硬币可以翻的其他硬币的编号必须是c的倍数。
那么如果两个单一游戏的c不同的话,只要a和b相同,那么状态的sg值也相同,所以存sg函数的状态时不用考虑c,只用考虑a和b即可。
暴力枚举子状态进行转移。
小神说这是博弈里比较水的一道题QwQ

#include<cstdio>
#include<vector>
#include<cstring>
#include<algorithm>
using namespace std;
int in() {
	int k = 0; char c = getchar();
	for(; c < '0' || c > '9'; c = getchar());
	for(; c >= '0' && c <= '9'; c = getchar())
		k = k * 10 + c - 48;
	return k;
}

vector <int> a;
int sg[23][23][23], n, maxq;

void init(int SG[23][23]) {
	int p, q, t = 0, tmp, num;
	for(int i = 0; i <= 20; ++i)
		for(int j = 0; j <= 20; ++j) {
			a.clear();
			for(p = 1; p <= i; ++p, t = 0)
				for(q = 1; q <= maxq && p * q <= i; ++q) {
					t ^= SG[i - p * q][j];
					a.push_back(t);
				}
			for(p = 1; p <= j; ++p, t = 0)
				for(q = 1; q <= maxq && p * q <= j; ++q) {
					t ^= SG[i][j - p * q];
					a.push_back(t);
				}
			
			stable_sort(a.begin(), a.end());
			if (!a.size() || a[0] != 0) continue;
			tmp = num = 1;
			while (tmp < a.size() && a[tmp] == a[tmp - 1]) ++tmp;
			while (tmp < a.size()) {
				if (a[tmp] != num) {
					SG[i][j] = num;
					break;
				}
				++tmp;
				while (tmp < a.size() && a[tmp] == a[tmp - 1])
					++tmp;
				++num;
			}
			if (tmp == a.size()) SG[i][j] = num;
		}
}

int T, m, pw2, pw3, ans;

int main() {
	for(maxq = 1; maxq <= 20; ++maxq)
		init(sg[maxq]);

	T = in();
	while (T--) {
		n = in(); maxq = in(); ans = 0;
		for(int i = 1; i <= n; ++i) {
			if (in()) continue;
			pw2 = pw3 = 0;
			m = i;
			while (m % 2 == 0) ++pw2, m /= 2;
			while (m % 3 == 0) ++pw3, m /= 3;
			ans ^= sg[maxq][pw2][pw3];
		}
		puts(ans ? "win" : "lose");
	}
		
	return 0;
}
原文地址:https://www.cnblogs.com/abclzr/p/5947144.html