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;
}
posted @ 2018-09-01 14:10  dummyummy  阅读(337)  评论(0编辑  收藏  举报