SG函数

来源:互联网 发布:apm源码分析位置控制 编辑:程序博客网 时间:2024/05/07 08:36

参考:

http://www.cnblogs.com/frog112111/p/3199780.html

http://cyan.logdown.com/posts/286252-combinatorial-games-with-the-sg-function


hdu 1848
bzoj 1188
bzoj 3576


SG SG?SG SG!


hdu 1848

直接求 sg 函数,然后判断游戏和即可。


const int maxn = 16, size = 1000;int fb[maxn + 5] = {1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987}, sg[size + 5];void prework(){    static bool hash[size + 5];    memset(hash, false, sizeof(hash));    for(int i = 1; i <= size; i++)    {        for(int j = 1; j < maxn && fb[j] <= i; j++)            hash[sg[i - fb[j]]] = true;        for(int j = 0; j <= i; j++)            if(!hash[j]) {sg[i] = j; break;}        for(int j = 1; j < maxn && fb[j] <= i; j++)            hash[sg[i - fb[j]]] = false;            }}int main(){    int m, n, q;#ifndef ONLINE_JUDGE        freopen("1848.in","r",stdin);    freopen("1848.out","w",stdout);#endif    prework();    read(m), read(n), read(q);    do    {        puts((sg[m]^sg[n]^sg[q]) ? "Fibo" : "Nacci");        read(m), read(n), read(q);    }while(m + n + q);#ifndef ONLINE_JUDGE        fclose(stdin);    fclose(stdout);#endif      return 0;}


bzoj 1188


考虑在一个位置的瓶子,

按每个瓶子中的巧克力豆个数的奇偶性分类,
巧克力豆个数为奇数时的 sg 函数值都相同,
巧克力豆个数为偶数时时 sg 函数值为 0。

然后求模拟玩法求每个位置巧克力豆个数为奇数时 sg 函数值即可。


  • 如何确定游戏先手能否获胜?
    ans 为巧克力豆个数为奇数的位置的 sg 函数值的异或和。
    如果 ans>0,那么 先手有必胜策略。

  • 如何输出第一步的操作?
    我们希望后继状态的 sg 值为 0,那么当 anssg(i)sg(j)sg(k)=0(i<jk)时,
    ijk 三个瓶子操作可以保证先手必胜。


const int maxn = 30;int n, sg[maxn], a[maxn];int hash[maxn*maxn];void prework(int size){    for(int i = 1; i <= size; i++)    {        for(int j = 1; j < i; j++)            for(int k = 1; k <= j; k++)                hash[sg[j] ^ sg[k]] = i;        for(int j = 0; j <= i*i; j++)            if(hash[j] != i)            {                sg[i] = j;                break;            }    }}void init(){    read(n);    for(int i = 1; i <= n; i++) read(a[i]);}void solve(){    int s1 = 0, s2 = 0, s3 = 0;    int ans = 0, cnt = 0;    for(int i = 1; i <= n; i++) if(a[i]&1) ans ^= sg[n - i + 1];    if(!ans) {puts("-1 -1 -1"), puts("0"); return;}    for(int i = 1; i <= n; i++)        for(int j = i + 1; j <= n; j++)            for(int k = j; k <= n; k++)                if(!(ans ^ sg[n - i + 1] ^ sg[n - j + 1] ^ sg[n - k + 1]))                    if(!cnt++) s1 = i, s2 = j, s3 = k;    write(s1 - 1), putchar(' ');    write(s2 - 1), putchar(' ');    write(s3 - 1), puts("");    write(cnt), puts("");           }int main(){    int T;#ifndef ONLINE_JUDGE    freopen("1188.in","r",stdin);    freopen("1188.out","w",stdout);#endif    read(T), prework(maxn - 5);    while(T--) init(), solve(); #ifndef ONLINE_JUDGE    fclose(stdin);    fclose(stdout);#endif    return 0;   }

bzoj 3576

这道题稍难一些。


设石子序列为{ai}, 暴力求 sg 函数 时间复杂度是 O(a2i)

如果把 x 个石子分成 p 堆。

sg(x)=mex{sg(x/p+1)(x mod p mod 2)sg(x/p)(1x mod p mod 2)|2px}

考虑 sg(x/p)sg(x/p+1)x/p 的不同取值是 O(x) 的。


证明:

  • p<x时,显然 x/p 最多只有 x 个取值

  • px时 , x/pm,注意到 x/p 是整数,所以最多也只有 x 个取值

做数学题时的常用优化方法。。。


x mod p mod 2 的取值只有两种,枚举一下就行了

另外我们还发现 Tn<=105 , 显然记忆化搜索会比直接打表快很多。


时间复杂度: O(aiai)



const int maxs = 1e5 + 50, size = maxs<<1, maxn = 110;int T, f;int n, a[maxn];#define work(t, c) (sg(((t) / (c) + 1)*((t) % (c) &1))^sg((t) / (c)*((c) - (t) % (c) &1)))int sg(int x){    static bool flag[maxs] = {false};    static int hash[size] = {0}, _sg[maxs];    if(x < f) return 0;    if(flag[x]) return _sg[x];    flag[x] = true;    for(int i = 2; i <= x; i = x/(x/i) + 1)    {        hash[work(x, i)] = x;        if(i < x) hash[work(x, i + 1)] = x;     }    int pos = 0;    while(hash[pos] == x) pos++;    return (_sg[x] = pos);}int solve(){    int ans = 0;    read(n);    for(int i = 1; i <= n; i++)        read(a[i]), ans ^= sg(a[i]);    return ans; }int main(){#ifndef ONLINE_JUDGE        freopen("3576.in","r",stdin);    freopen("3576.out","w",stdout);#endif    read(T), read(f);    while(T--) putchar('0' + (solve()?1:0)), putchar(T?' ':'\n');#ifndef ONLINE_JUDGE    fclose(stdin);    fclose(stdout);#endif    return 0;       }

0 0
原创粉丝点击