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

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)$个。

 1 #include<cstdio>
 2 #include<cstdlib>
 3 #include<cstring>
 4 #include<iostream>
 5 #include<algorithm>
 6 #include<cmath>
 7 using namespace std;
 8 #define Maxn 500010
 9 
10 int n,sq,sg[2][Maxn];
11 int w[Maxn],wl;
12 bool vis[Maxn];
13 
14 int nxt(int x,int y) {return x==y?y+1:y/(y/(x+1));}
15 
16 void init()
17 {
18     memset(vis,0,sizeof(vis));
19     for(int i=1;i<=n;i=nxt(i,n))
20     {
21         wl=0;int now=0;
22         for(int j=2;j<=i;j=nxt(j,i))
23         {
24             int x=i/j;
25             int nw=(x>sq)?sg[1][n/x]:sg[0][x];
26             // if(!((i/x-i/(x+1))&1)) continue;
27             w[++wl]=now^nw;
28             vis[w[wl]]=1;
29             // now^=nw;
30             if((i/x-i/(x+1))&1) now^=nw;
31         }
32         now=1;
33         while(vis[now]) now++;
34         if(i>sq) sg[1][n/i]=now;
35         else sg[0][i]=now;
36         for(int j=1;j<=wl;j++) vis[w[j]]=0;
37     }
38 }
39 
40 int main()
41 {
42     scanf("%d",&n);
43     sq=(int)sqrt((double)n);
44     init();
45     int T;
46     scanf("%d",&T);
47     while(T--)
48     {
49         int sm,ans=0;scanf("%d",&sm);                                                                                                                
50         while(sm--)
51         {
52             int x;
53             scanf("%d",&x);
54             ans^=(n/x>sq)?sg[1][x]:sg[0][n/x];
55         }
56         if(ans) printf("Yes
");
57         else printf("No
");
58     }
59     return 0;
60 }
View Code

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

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

2017-04-11 10:58:52

原文地址:https://www.cnblogs.com/Konjakmoyu/p/6692581.html