哎,被卡科技了,想了三个小时,最后还是大佬给我说是(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;
}