Wannafly挑战赛23 T2游戏 SG函数

Posted dummyummy

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Wannafly挑战赛23 T2游戏 SG函数相关的知识,希望对你有一定的参考价值。

哎,被卡科技了,想了三个小时,最后还是大佬给我说是(SG)函数。
(SG)函数,用起来很简单,证明呢?(不可能的,这辈子都是不可能的)

(SG)定理

游戏的(SG)函数就是各个子游戏的(SG)函数的(Nim-sum)(就是异或和),比如多堆石子的(SG)函数就是所有单堆石子(SG)函数的异或和。

(SG)函数

首先定义(mex(T))(T)中未出现的自然数中最小的数,其中(T subset N),如(mex(0,2,3)=1)(mex(4,7)=0)。那么(SG(x)=mex(S))(S)(x)的后继状态的(SG)函数值集合。
然后,对于某一个状态(x),若(SG(x)=0),则先手必败,否则先手必胜。
对于这一道题,因为只能拿约数个,也就是至少拿一个,所以我们可以先从小到大预处理一下(SG)函数。然后因为我们是先手,要给对手制造一个必败状态,所以只要枚举一下第一步的所有策略,然后判断一下剩下的那些石子的(SG(x))函数的异或和是否为(0)就行了。
期望复杂度(O(n^{frac{3}{2}}))

#include <bits/stdc++.h>
 
using namespace std;
 
const int N = 100000;
 
int n, a[N+5], sum, p, ans, SG[N+5], vis[N+5];
 
void calc_SG() { //预处理SG函数
    SG[0] = 0, SG[1] = 1; //初始化
    for(int i = 2; i <= N+1; i++) {
        int cnt = 0, t = 0x3f3f3f3f;
        for(int j = 1; j*j <= i; j++)
            if(i%j == 0) {
                vis[++cnt] = SG[i-j];
                if(j*j != i) vis[++cnt] = SG[i-i/j];
            }
        sort(vis+1, vis+cnt+1);
        if(vis[1] >= 1) { //计算mex(T)
            SG[i] = 0;
            continue;
        }
        for(int j = 1; j <= cnt-1; j++)
            if(vis[j+1]-vis[j] > 1) t = min(t, vis[j]+1);
        t = min(t, vis[cnt]+1);
        SG[i] = t;
    }
}
 
int main() {
    ios_base::sync_with_stdio(false), cin.tie(0), cout.tie(0); //读入输出加速
    cin >> n;
    calc_SG(); //预处理SG函数
    for(int i = 1; i <= n; i++) cin >> a[i], sum ^= SG[a[i]];
    for(int i = 1; i <= n; i++) {
        p = sum^SG[a[i]];
        for(int j = 1; j*j <= a[i]; j++) {
            if(a[i]%j) continue;
            if((p^SG[a[i]-j]) == 0) ans++; //判断剩下的Nim-sum是否为0
            if(j*j != a[i] && (p^SG[a[i]-a[i]/j]) == 0) ans++;
        }
    }
    cout << ans << endl;
    return 0;
}

以上是关于Wannafly挑战赛23 T2游戏 SG函数的主要内容,如果未能解决你的问题,请参考以下文章

NowCoder Wannafly挑战赛23 体验记

Wannafly挑战赛27

Wannafly挑战赛24-A-石子游戏--思维题

Wannafly挑战赛7 C - 小Q与氪金游戏

wannafly 挑战赛10 小H和游戏

Wannafly挑战赛26