[HAOI2015]数组游戏
题目大意:
有一排n个格子,每个格子上都有一个白子或黑子,在上面进行游戏,规则如下:
选择一个含白子的格子x,并选择一个数k,翻转x,2x,...,kx格子上的子。
不能操作者负。
思路:
将“某个格子上有一个白子 ”视作游戏的一个状态。
对于状态x,sg(x)=mex{sg(2x),sg(2x)^sg(3x),sg(2x)^sg(3x)^...^sg(kx)}。
由于SG函数的取值只与棋盘大小和棋子位置有关,因此我们可以记忆化。
然后我们就有了暴力构造SG函数的程序,实测只能过40%的点,大力卡常以后勉强能50%。
而且空间显然也开不下,只能用hash_map。
然而这题的SG函数有一些神奇的性质,例如,对于大小为10的棋盘,当x分别为1~10时,sg(x)分别为:
4 1 2 2 2 1 1 1 1 1
我们将它们进行分组:
(4)(1)(2)(2 2)(1 1 1 1 1)
可以发现,当不同棋子可以往后跳的步数相同时,它们的SG函数相同。
显然可以把它们分为2sqrt(n)组。
其中前sqrt(n)组都是一个子一组,后sqrt(n)组都是很多个一组。
这样,以sqrt(n)为界,对于我们需要的函数,判断一下参数的范围即可。
1 #include<cmath> 2 #include<cstdio> 3 #include<cctype> 4 inline int getint() { 5 char ch; 6 while(!isdigit(ch=getchar())); 7 int x=ch^'0'; 8 while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^'0'); 9 return x; 10 } 11 const int N=100000; 12 int sg[N][2],mex[N]; 13 int n,block; 14 inline int next(const int &x,const int &y) { 15 return x==y?x+1:y/(y/(x+1)); 16 } 17 void getsg() { 18 for(register int i=1;i<=n;i=next(i,n)) { 19 for(register int k=2,tmp=0;k<=i;k=next(k,i)) { 20 int x=i/k; 21 int t=(x>block)?sg[n/x][1]:sg[x][0]; 22 mex[tmp^t]=i; 23 if((i/x-i/(x+1))&1) tmp^=t; 24 } 25 int tmp=1; 26 while(mex[tmp]==i) tmp++; 27 ((i>block)?sg[n/i][1]:sg[i][0])=tmp; 28 } 29 } 30 int main() { 31 n=getint(); 32 block=floor(sqrt(n)); 33 getsg(); 34 for(register int m=getint();m;m--) { 35 int ans=0; 36 for(register int w=getint();w;w--) { 37 const int x=getint(); 38 ans^=(n/x>block)?sg[n/(n/x)][1]:sg[n/x][0]; 39 } 40 puts(ans?"Yes":"No"); 41 } 42 return 0; 43 }
暴力构造SG函数的程序:
1 #include<cmath> 2 #include<cstdio> 3 #include<cctype> 4 #include<cstring> 5 #include<ext/hash_map> 6 inline int getint() { 7 char ch; 8 while(!isdigit(ch=getchar())); 9 int x=ch^'0'; 10 while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^'0'); 11 return x; 12 } 13 __gnu_cxx::hash_map<int,int> sg,mex; 14 int n; 15 int getsg(const int x) { 16 if(sg.count(x)) return sg[x]; 17 for(int k=2,tmp=0;k<=n/x;k++) { 18 tmp^=getsg(x*k); 19 mex[tmp]=x; 20 } 21 for(sg[x]=1;mex[sg[x]]==x;sg[x]++); 22 return sg[x]; 23 } 24 int main() { 25 n=getint(); 26 for(int i=n;i;i--) getsg(i); 27 for(int m=getint();m;m--) { 28 int ans=0; 29 for(int w=getint();w;w--) { 30 ans^=getsg(getint()); 31 } 32 puts(ans?"Yes":"No"); 33 } 34 return 0; 35 }