小A是一个名副其实的狂热的回合制游戏玩家。在获得了许多回合制游戏的世界级奖项之后,小A有一天突然想起了他小时候在江南玩过的一个回合制游戏。
游戏的规则是这样的,首先给定一个数F,然后游戏系统会产生T组游戏。每一组游戏包含N堆石子,小A和他的对手轮流操作。每次操作时,操作者先选定一个不小于2的正整数M (M是操作者自行选定的,而且每次操作时可不一样),然后将任意一堆数量不小于F的石子分成M堆,并且满足这M堆石子中石子数最多的一堆至多比石子数最少的一堆多1(即分的尽量平均,事实上按照这样的分石子万法,选定M和一堆石子后,它分出来的状态是固定的)。当一个玩家不能操作的时候,也就是当每一堆石子的数量都严格小于F时,他就输掉。(补充:先手从N堆石子中选择一堆数量不小于F的石子分成M堆后,此时共有N+M-1)堆石子,接下来小A从这N+M-1堆石子中选择一堆数量不小于F的石子,依此类推。
小A从小就是个有风度的男生,他邀请他的对手作为先手。小A现在想要知道,面对给定的一组游戏,而且他的对手也和他一样聪明绝顶的话,究竟谁能够获得胜利?
输入格式
输入第一行包含两个正整数T和F,分别表示游戏组数与给定的数。 接下来T行,每行第一个数N表示该组游戏初始状态下有多少堆石子。之后N个正整数,表示这N堆石子分别有多少个。
输出格式
输出一行,包含T个用空格隔开的0或1的数,其中0代表此时小A(后手)会胜利,而1代表小A的对手(先手)会胜利。
输入输出样例
输入 #1复制
4 3 1 1 1 2 1 3 1 5
输出 #1复制
0 0 1 1
说明/提示
对于100%的数据,T<100,N<100,F<100000,每堆石子数量<100000。
以上所有数均为正整数。
一次操作可以将一堆数量为 ii(i\ge Fi≥F)的石子分为 j-i\bmod jj−imodj 堆数量为 \left\lfloor\frac{i}{j}\right\rfloor⌊ji⌋ 的石子和 i\bmod jimodj 堆数量为 \left\lceil\frac{i}{j}\right\rceil⌈ji⌉ 的石子,其中 jj 是每次操作时玩家自行选定,满足 2\le j\le i2≤j≤i,FF 为给定。不能操作者输。
初始有 nn 堆石子,第 ii 堆石子的数量为 a_iai。多组数据,但 FF 不改变。
T,n\le100T,n≤100,F,a_i\le10^5F,ai≤105.
思路
我们定一个数的 \text{SG}SG 值为 f(x)f(x),一个局面的 \text{SG}SG 值为 f(state)=\bigoplus_{i\in state}f(i)f(state)=⨁i∈statef(i)。
于是求出每个数的 \text{SG}SG 值即可。
首先,对于 i<Fi<F,f(i)=0f(i)=0,因为显然此时无法取到石子,先手必败。
然后枚举 jj,我们可以算出此时划分的局面的 \text{SG}SG 值。因为只有两种棋子的取值,且知道每堆的数量,所以算此时划分的局面的 \text{SG}SG 值是 O(1)O(1) 的。
令 v=\max\{a_i\}v=max{ai},则此时时间复杂度为 O(v^2+Tn)O(v2+Tn),无法通过。
瓶颈在于计算 \text{SG}SG,我们观察式子,发现有 \left\lfloor\frac{i}{j}\right\rfloor⌊ji⌋ 这一形式,于是考虑整除分块。
但是式子里还有 i\bmod jimodj 这一形态,整除分块不好处理,怎么办呢?
考虑向奇偶性方向想,
- 当 2\nmid\left\lfloor\frac{i}{j}\right\rfloor2∤⌊ji⌋ 时,j-i\bmod j=j-(i-j\left\lfloor\frac{i}{j}\right\rfloor)=j(1+\left\lfloor\frac{i}{j}\right\rfloor)-ij−imodj=j−(i−j⌊ji⌋)=j(1+⌊ji⌋)−i,发现此时 j-i\bmod jj−imodj 的奇偶性不变,则此部分贡献不变;
- 当 2|\left\lfloor\frac{i}{j}\right\rfloor2∣⌊ji⌋ 时,i\bmod j=i-j\left\lfloor\frac{i}{j}\right\rfloorimodj=i−j⌊ji⌋,则 i\bmod jimodj 奇偶性不变,此部分贡献不变。
发现无论 \left\lfloor\frac{i}{j}\right\rfloor⌊ji⌋ 的奇偶性,两种数量的石子的堆数中总有一种不变。
于是我们只需要枚举 22 个 jj 值就能代表所有情况了。
求 \text{mex}mex 可以暴力,因为集合里的数的个数为 O(\sqrt v)\times O(1)=O(\sqrt v)O(v)×O(1)=O(v)。
此时时间复杂度降为 O(v\sqrt v+Tn)O(vv+Tn),可以通过。
由于查询的 a_iai 值较少,不会涵盖整个值域,可以使用记忆化搜索,比递推快很多。
代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
namespace IO{//by cyffff
}
const int N=1e5+10;
int n,T,f;
int SG[N];
inline int getSG(int i){
if(i<f) return 0;
if(~SG[i]) return SG[i];
bool vis[100];
memset(vis,0,sizeof(vis));
for(int j=2,r;j<=i;j=r+1){
r=i/(i/j);
int res=0;
bool flag=i%j;
if(i%j&1) res^=getSG(i/j+1);
if(j-i%j&1) res^=getSG(i/j);
j++;
vis[res]=i;
if(flag){
res=0;
if(i%j&1) res^=getSG(i/j+1);
if(j-i%j&1) res^=getSG(i/j);
j++;
vis[res]=i;
}
}
for(int j=0;;j++)
if(!vis[j])
return SG[i]=j;
}
int main(){
memset(SG,-1,sizeof(SG));
T=read(),f=read();
while(T--){
n=read();
int res=0;
for(int i=1;i<=n;i++){
res^=getSG(read());
}
write(res>0),putc(' ');
}
flush();
}