SG函数学(hua)习(shui)记录

---恢复内容开始---

听说有一个东西叫SG函数

觉得自己好像原来是懂一些粗浅的应用但现在感觉要再深♂入一点呢

让我们先来介绍一下SG函数吧

这是某类满足下列条件的玄学博弈问题解法

  • 双人、回合制;
  • 信息完全公开(perfect information);
  • 无随机因素(deterministic);
  • 必然在有限步内结束(finite);
  • 没有平局;
  • 双方可采取的行动相同(impartial);
  • 无法行动者判负(normal play);

具体证明可以见https://zhuanlan.zhihu.com/p/20611132?columnSlug=maigo

那么我们现在有一个函数SG[i]表示某种状态的SG函数

且有SG[i] = mex{SG[k] | k->i(k是i的下一级状态)}   SG[i] = SG[a1]^SG[a2]^SG[a3]^...^SG[ak] (a1...ak是组成状态i的子状态)

如果SG[i] == 0 那么 i 是一种必败态,不然是一种必胜态

于是介绍完了

下面上例题

 http://www.lydsy.com/JudgeOnline/problem.php?id=1022

 1 #include<stdio.h>  
 2 #include<stdlib.h>  
 3 int main()  
 4 {  
 5     int T,n,i,x,SG,flag;  
 6     scanf("%d",&T);  
 7     for(;T>0;T--)  
 8     {  
 9         scanf("%d",&n);  
10         SG=flag=0;   
11         for(i=1;i<=n;i++)  
12         {  
13             scanf("%d",&x);  
14             SG^=x;  
15             if(x!=1) flag=1; 
16         }  
17         if( (SG==0&&flag==0) || (SG!=0&&flag==1) ) printf("John\n");  
18         else printf("Brother\n");  
19     }  
20     return 0;  
21 }  
BZOJ 1022

 这是一道最简单的SG函数题,连SG函数都不用存就可以一路推推推推出终态的SG函数

 

http://www.lydsy.com/JudgeOnline/problem.php?id=4035

 1 #include<bits/stdc++.h>
 2 #define N 100010
 3 using namespace std;
 4 int n,T,SG1[N],SG2[N],m;
 5 
 6 int read(){
 7     int f = 0; char ch = getchar();
 8     while (ch > '9' || ch < '0' ) ch = getchar();
 9     while (ch <= '9'&& ch >= '0') {
10         f = f*10 + ch - '0';
11         ch = getchar();
12     }
13     return f;
14 }
15 
16 int nxt(int x,int y){ return (x==y)?y+1:y/(y/(x+1)); }  
17 
18 void GetSG(){ 
19     int now,cnt,a[N]; bool bo[N];
20     memset(SG1,0,sizeof(SG1));
21     memset(bo,0,sizeof(bo));
22     memset(SG2,0,sizeof(SG2));
23     for (int i=1; i<=n; i=nxt(i,n)){  
24         now=cnt=0;  
25         for (int j=2; j<=i; j=nxt(j,i)){  
26             int x=i/j; int t=(x>m)?SG2[n/x]:SG1[x];  
27             a[++cnt]=now^t; bo[a[cnt]]=1;  
28             if ((i/x-i/(x+1))&1) now^=t;  
29         }  
30         now=1; while (bo[now]) now++; 
31         (i <= m) ? SG1[i] = now : SG2[n/i]=now;  
32         for (int j=1; j<=cnt; j++) bo[a[j]]=0;  
33     }  
34 }  
35 
36 int main(){
37     n = read();    T = read(); m = (int)sqrt(n); GetSG(); 
38     while (T--){
39         int cnt = read(),ans = 0,x;
40         for (int i = 1; i <= cnt; i ++){
41             x = n/read();
42             ans^=(x>m)?SG2[n/x]:SG1[x];
43         }
44         puts((ans)?"Yes":"No");
45     }
46 }
BZOJ4035

这题可以对每一个白格子位置搞SG:SG[i]=mex{SG[i*1]^SG[i*2]^...^SG[i*k]},k∈[2,N/i]

但是我们会发现状态太多存不下

所以我们可以再推一推,发现每个SG函数只和n/i有关,所以状态数就变成了N^0.5个,大于N^0.5的状态就存在n/i里就好了

然后对于每一个状态,有很多状态都是冗余的,不需要计算,所以在循环的时候直接写成像这样就好了:

 for (int i = 1; i <= n; i = n/(n/(i+1))) // 记得判 i == n时不要让程序计算n/(n/(i+1))不然会除0 

——————————————————————————————————未完待 θ.θ 续——————————————————————————————————

 

posted @ 2017-04-22 11:09  xc01  阅读(287)  评论(0编辑  收藏  举报