3438 数组游戏
有一个长度为N的数组,甲乙两人在上面进行这样一个游戏:首先,数组上有一些格子是白的,有一些是黑的。然后两人轮流进行操作。每次操作选择一个白色的格子,假设它的下标为x。接着,选择一个大小在1~n/x之间的整数k,然后将下标为x、2x、…、kx的格子都进行颜色翻转。不能操作的人输。现在甲(先手)有一些询问。每次他会给你一个数组的初始状态,你要求出对于这种初始状态他是否有必胜策略。
输入
第一行一个整数N ,表示数组的长度。
第二行一个整数 K ,表示询问的个数。
接下来 2K 行,每两行表示一次询问。
在这两行中,第一行一个正整数 W,表示数组中有多少个格子是白色的,第二行则有W 个
1~N 之间的正整数,表示白色格子的对应下标。
输出
对于每个询问,若先手必胜输出Yes,否则输出No。答案之间用换行隔开。
数据范围
对于30%的数据,N<=20;
对于50%的数据,N<=1000000;
对于70%的数据,N<=10000000;
对于 100% 的数据,N<= 1000000000,K,W<=100 ,不会有格子在同一次询问中多次出现。
输入样例
3
2
2
1 2
2
2 3
输出样例
Yes
No
这里不放解析,直接放代码:
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<cstdio>
#include<climits>
#include<ctime>
using namespace std;
template<typename T>bool chkmax(T &_,T __){return _<__ ? (_=__,1) : 0;}
template<typename T>bool chkmin(T &_,T __){return _>__ ? (_=__,1) : 0;}
#define REP(i,a,b) for(register int i=a;i<=b;++i)
#define DREP(i,a,b) for(register int i=a;i>=b;--i)
#define MREP(i,x) for(register int i=beg[x];i;i=E[i].last)
#define mem(a) memset(a,0,sizeof(a))
#define ll long long
#define inf INT_MAX
const int maxn=1e9+10;
const int maxm=100+10;
int n,q,m,a[maxm],SG[maxm*maxm],L[maxn/10000],R[maxn/10000],cnt;
int va1[maxn/10000],va2[maxn/10000],block;
bool in[maxn/10000];
int get(int x){return x<=block ? va1[x] : va2[n/x];}
void into(int x,int va){
if(x<=block)va1[x]=va;
else va2[n/x]=va;
}
void init(){
for(int l=1,r;l<=n;l=r+1){
r=n/(n/l);
++cnt;
L[cnt]=l;
R[cnt]=r;
}
DREP(i,cnt,1){
mem(in);
int b=n/L[i],sum=0;
for(int l=1,r;l<=b;l=r+1){
int c=b/l,tmp=get(L[i]*l);
r=b/c;
in[sum]=1;
in[sum^tmp]=1;
if((r-l+1)%2)sum^=tmp;
}
REP(j,0,maxn)if(!in[j]){
into(L[i],j);
break;
}
}
}
int main(){
File();
scanf("%d%d",&n,&q);
block=sqrt(n);
init();
REP(i,1,q){
int ans=0;
scanf("%d",&m);
REP(j,1,m){
int u;
scanf("%d",&u);
ans^=get(u);
}
if(ans)puts("Yes");
else puts("No");
}
return 0;
}