【BZOJ 4035】 4035: [HAOI2015]数组游戏 (博弈)

时间:2024-09-01 22:04:02

4035: [HAOI2015]数组游戏

Time Limit: 15 Sec  Memory Limit: 32 MB
Submit: 181  Solved: 89

Description

有一个长度为N的数组,甲乙两人在上面进行这样一个游戏:首先,数组上有一些格子是白的,有一些是黑的。然
后两人轮流进行操作。每次操作选择一个白色的格子,假设它的下标为x。接着,选择一个大小在1~n/x之间的整数
k,然后将下标为x、2x、...、kx的格子都进行颜色翻转。不能操作的人输。现在甲(先手)有一些询问。每次他
会给你一个数组的初始状态,你要求出对于这种初始状态他是否有必胜策略。

Input

接下来2*K行,每两行表示一次询问。在这两行中,第一行一个正整数W,表示数组中有多少个格子是白色的,第二
行则有W个1~N之间的正整数,表示白色格子的对应下标。

Output

对于每个询问,若先手必胜输出"Yes",否则输出"No"。答案之间用换行隔开

Sample Input

3
2
2
1 2
2
2 3

Sample Output

Yes
No
【样例解释】
在第一个询问中,甲选择点1,然后将格子1*1和2*1翻过来即可。
第二个询问中,无论甲选择哪个点,都只能翻掉一个格子。乙只需
翻掉另一个格子就行了。
N<=1000000000 , K,W<=100 , 不会有格子在同
一次询问中多次出现。

HINT

Source

【分析】

  

  把每一个白点看成是一个子游戏,最后将SG函数全部异或起来即可,由SG定理可知有:

$SG(i)$ = $mex${$SG[i*1]$^$SG[i*2]$^$...$ ^ $SG[i*k]$},$k=[2,N/i]$。

  这个第一步应该想到的,但是这样子直接暴力不行。

  实际上某一个SG[i]函数的值只和N/i有关,因此有用的状态只有$O(\sqrt n)$个。

 #include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;
#define Maxn 500010 int n,sq,sg[][Maxn];
int w[Maxn],wl;
bool vis[Maxn]; int nxt(int x,int y) {return x==y?y+:y/(y/(x+));} void init()
{
memset(vis,,sizeof(vis));
for(int i=;i<=n;i=nxt(i,n))
{
wl=;int now=;
for(int j=;j<=i;j=nxt(j,i))
{
int x=i/j;
int nw=(x>sq)?sg[][n/x]:sg[][x];
// if(!((i/x-i/(x+1))&1)) continue;
w[++wl]=now^nw;
vis[w[wl]]=;
// now^=nw;
if((i/x-i/(x+))&) now^=nw;
}
now=;
while(vis[now]) now++;
if(i>sq) sg[][n/i]=now;
else sg[][i]=now;
for(int j=;j<=wl;j++) vis[w[j]]=;
}
} int main()
{
scanf("%d",&n);
sq=(int)sqrt((double)n);
init();
int T;
scanf("%d",&T);
while(T--)
{
int sm,ans=;scanf("%d",&sm);
while(sm--)
{
int x;
scanf("%d",&x);
ans^=(n/x>sq)?sg[][x]:sg[][n/x];
}
if(ans) printf("Yes\n");
else printf("No\n");
}
return ;
}

不会打,膜了一下lych_cys的代码。。

判断&1那里是如果有偶数个相同的,异或起来等于0,就不用异或进去了。

2017-04-11 10:58:52