因为队里只有一个人搞博弈的sg,所以我也来学一下了
/*大佬请无视这段解释sg是个什么东西*/
sg解决的事类nim博弈问题
所谓的nim博弈一般就是:
有若干堆石子,每堆石子的数量都是有限的,合法的移动是“选择一堆石子并拿走若干颗(不能不拿)”,
如果轮到某个人时所有的石子堆都已经被拿空了,则判负(因为他此刻没有任何合法的移动)。
然后类nim就是在拿石子上做功夫,
(什么限定拿1到m啦,
( 限定拿规定的个数了
(比如说斐波那契数列、或者说下面这个题里的,只能拿(比自己小的并且和自己互质的数)或者是(自己)
nim博弈的解决办法是将所有数异或起来,如果是0,那么先手必败,否则先手必胜
而类nim博弈,是将所有数的sg值异或起来,也就是说可以将nim博弈看成类nim博弈的特例
而我们要做的就是把这个sg表(一维数组)打出来
下面是打表代码,f数组是每一堆可以拿的个数(具体解释的话这里是、链接)
const int maxn = 1e5+7,mod = 1e9+7;
//f[]:可以取走的石子个数
//sg[]:0~n的SG函数值
//Hash[]:mex{}
int f[maxn],sg[maxn],Hash[maxn];
void initSG(int n){
memset(sg,0,sizeof(sg));
for(int i=1;i<=n;i++){//当前堆中有i个石头
memset(Hash,0,sizeof(Hash));
for(int j=1;f[j]<=i;j++)
Hash[sg[i-f[j]]]=1;
for(int j=0;j<=n;j++)if(Hash[j]==0){//求mes{}中未出现的最小的非负整数
sg[i]=j;
break;
}
}
}
那么关于这个题
(来源是:2019年杭州师范大学第十二届程序设计竞赛,然后upc补题场结束后说不会再开了,所以链接就不贴了)
题意:
类nim博弈,每一堆可以拿的个数是与当前堆中个数互质且小于当前堆中的个数,或者删除一个堆
(也就是当前堆全部拿走)
题解:sg打表找规律
sg打表代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5+7,mod = 1e9+7;
//f[]:可以取走的石子个数
//sg[]:0~n的SG函数值
//Hash[]:mex{}
int f[maxn],sg[maxn],Hash[maxn];
void initSG(int n){
memset(sg,0,sizeof(sg));
for(int i=1;i<=n;i++){
memset(Hash,0,sizeof(Hash));
//for(j=1;f[j]<=i;j++)
// Hash[sg[i-f[j]]]=1;
for(int j=1;j<=i;j++)if(__gcd(j,i) == 1 || j == i)
Hash[sg[i-j]] = 1;
for(int j=0;j<=n;j++)if(Hash[j]==0){//求mes{}中未出现的最小的非负整数
sg[i]=j;
break;
}
}
}
int main(){
initSG(1000);
for(int i=1;i<=30;i++)printf("%3d-->%3d\n",i,sg[i]);
return 0;
}
一个显然的规律是
1、对于一个合数:sg[x] = sg[y](y是x的最小质因子)
2、对于一个质数:显然是sg[x] = sg[y]+1(y是小于x的最大质数)
一个欧拉筛,然后就解决了
#include <bits/stdc++.h>
typedef long long ll;
using namespace std;
const int maxn = 1e6+7;
bool vis[maxn];
int prim[maxn],cnt,SG[maxn]={0,1},T,n,x;
void init(){
for(int i=2;i<maxn;i++){
if(!vis[i])prim[cnt++] = i,SG[i] = cnt+1;
for(int j=0;j<cnt&&i*prim[j]<maxn;j++){
vis[i*prim[j]] = 1,SG[i*prim[j]] = SG[prim[j]];
if(i%prim[j] == 0)break;
}
}
}
char s[2][50] = {"Subconscious is our king!","Long live with King Johann!"};
int main(){
init();
for(scanf("%d",&T);T--;){
scanf("%d",&n);
int ans = 0;
while(n--)
scanf("%d",&x),ans ^= SG[x];
printf("%s\n",s[ans?0:1]);
}
return 0;
}