Description
小 A 是一个名副其实的狂热的回合制游戏玩家。在获得了许多回合制游戏的世界级奖项之后,小 A 有一天突然想起了他小时候在江南玩过的一个回合制游戏。
游戏的规则是这样的,首先给定一个数 FF ,然后游戏系统会产生 组游戏。每一组游戏包含 NN 堆石子,小 和他的对手轮流操作。每次操作时,操作者先选定一个不小于 22 的正整数 ( MM 是操作者自行选定的,而且每次操作时可不一样),然后将任意一堆数量不小于 的石子分成 MM 堆,并且满足这 堆石子中石子数最多的一堆至多比石子数最少的一堆多 11 (即分的尽量平均,事实上按照这样的分石子万法,选定 和一堆石子后,它分出来的状态是固定的)。当一个玩家不能操作的时候,也就是当每一堆石子的数量都严格小于 FF 时,他就输掉。(补充:先手从 堆石子中选择一堆数量不小于 FF 的石子分成 堆后,此时共有 N+M−1N+M−1 堆石子,接下来小 A 从这 N+M−1N+M−1 堆石子中选择一堆数量不小于 FF 的石子,依此类推)
小 A 从小就是个有风度的男生,他邀请他的对手作为先手。小 A 现在想要知道,面对给定的一组游戏,而且他的对手也和他一样聪明绝顶的话,究竟谁能够获得胜利?
Input
输入第一行包含两个正整数 和 FF ,分别表示游戏组数与给定的数。
接下来 行,每行第一个数 NN 表示该组游戏初始状态下有多少堆石子。之后 个正整数,表示这 NN 堆石子分别有多少个。
Output
输出一行,包含 个用空格隔开的 00 或 的数,其中 00 代表此时小 A (后手)会胜利,而 代表小 A 的对手(先手)会胜利。
Sample Input
4 3
1 1
1 2
1 3
1 5
Sample Output
0 0 1 1
HINT
对于 100% 的数据, T<100,N<100,F<100000T<100,N<100,F<100000 ,每堆石子数量 <100000<100000 。
以上所有数均为正整数。
Solution
发现初始的 NN 堆石子分别都对应了一个独立的游戏,因此只需要把这 堆分开,计算它们的 SGSG 值后异或起来即可。
在读入 TT 组游戏之前先进行一个预处理 DP :
表示当前还剩下 ii 个石子的 值。
边界:当 0≤i<F0≤i<F 时, SG[i]=0SG[i]=0 。
转移:
其中 W(x,y)W(x,y) 的定义为:如果 yy 是奇数则 ,否则 W(x,y)=0W(x,y)=0 。
暴力转移是平方的,显然 TLE 。
但是可以发现 ⌊ij⌋⌊ij⌋ 的取值只有 i√i 种,可以考虑枚举 ⌊ij⌋⌊ij⌋ 的取值来计算。
假设现在枚举到了 ⌊ij⌋=k⌊ij⌋=k ,那么这时候 SG[k]SG[k] 和 SG[k+1]SG[k+1] 的值就能快速得到了。但我们还需要知道 imodjimodj 和 j−imodjj−imodj 的奇偶性。
分 jj 为奇数和偶数两种情况处理,这样知道了 的奇偶性也就知道了 j−imodjj−imodj 的奇偶性。
设当 j∈[l,r]j∈[l,r] 时, ⌊ij⌋=k⌊ij⌋=k 。
当 jj 为偶数(需判断 内是否有偶数)时比较显然,如果 ii 为偶数那么 也为偶数,否则为奇数。
当 jj 为奇数(需判断 内是否有奇数)时,如果 i−任意奇数×ki−任意奇数×k 得到偶数那么 imodjimodj 也为偶数,否则为奇数。
于是,就可以在根号的复杂度内完成一次转移了。
Code
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define For(i, a, b) for (i = a; i <= b; i++)
using namespace std;
inline int read() {
int res = 0; bool bo = 0; char c;
while (((c = getchar()) < '0' || c > '9') && c != '-');
if (c == '-') bo = 1; else res = c - 48;
while ((c = getchar()) >= '0' && c <= '9')
res = (res << 3) + (res << 1) + (c - 48);
return bo ? ~res + 1 : res;
}
const int N = 1e5 + 5, Z = 87;
int n, T, F, sg[N], tmp[Z];
void orzcyxdalao() {
int i, j; For (i, F, 100000) {
memset(tmp, 0, sizeof(tmp)); for (j = 2; j <= i;) {
int nxt = i / (i / j), p1 = sg[i / j], p2 = sg[i / j + 1], k = i / j;
if (!(j & 1) || j < nxt) tmp[i & 1 ? p1 ^ p2 : 0] = 1;
if ((j & 1) || j < nxt)
tmp[i - (j & 1 ? j : j + 1) * k & 1 ? p2 : p1] = 1;
j = nxt + 1;
}
For (j, 0, 2147483647) if (!tmp[j]) {sg[i] = j; break;}
}
}
int main() {
int i; T = read(); F = read(); orzcyxdalao();
while (T--) {
int ans = 0; n = read(); For (i, 1, n) ans ^= sg[read()];
printf(ans ? "1 " : "0 ");
}
cout << endl; return 0;
}