有一个长度为N的数组,甲乙两人在上面进行这样一个游戏:首先,数组上有一些格子是白的,有一些是黑的。然后两人轮流进行操作。每次操作选择一个白色的格子,假设它的下标为x。接着,选择一个大小在1~n/x之间的整数k,然后将下标为x、2x、...、kx的格子都进行颜色翻转。不能操作的人输。现在甲(先手)有一些询问。每次他会给你一个数组的初始状态,你要求出对于这种初始状态他是否有必胜策略。
Solution
用暴力 SG 打个表,发现一段一段的 SG 值都是相同的,所以套个整除分块即可
#include <bits/stdc++.h>
using namespace std;
const int N = 400005;
int n,q,lim,sg[2][N],pos[N],u[N];
int SG(int x) {
x=n/(n/x);
if(x>lim) return sg[1][n/x];
else return sg[0][x];
}
void presolve() {
int cnt=0;
for(int i=1,j;i<=n;i=j+1) {
j=n/(n/i);
pos[++cnt]=j;
}
while(cnt) {
int x=pos[cnt], now=0, mex=1;
u[now]=cnt;
for(int i=x+x,j;i<=n;i=j+x) {
j=n/(n/i)/x*x;
u[now^SG(j)]=cnt;
if((j-i)/x&1^1) now^=SG(j);
}
while(u[mex]==cnt) ++mex;
if(x>lim) sg[1][n/x]=mex;
else sg[0][x]=mex;
--cnt;
}
}
signed main() {
ios::sync_with_stdio(false);
cin>>n>>q;
lim=sqrt(n);
presolve();
while(q--) {
int m,x,ans=0;
cin>>m;
while(m--) {
cin>>x;
ans^=SG(x);
}
if(ans) cout<<"Yes"<<endl;
else cout<<"No"<<endl;
}
}