【BZOJ 4035】 4035: [HAOI2015]数组游戏 (博弈)
4035: [HAOI2015]数组游戏
Time Limit: 15 Sec Memory Limit: 32 MB
Submit: 181 Solved: 89Description
有一个长度为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 3Sample 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\n"); 57 else printf("No\n"); 58 } 59 return 0; 60 }
不会打,膜了一下lych_cys的代码。。
判断&1那里是如果有偶数个相同的,异或起来等于0,就不用异或进去了。
2017-04-11 10:58:52