P4279 [SHOI2008]小约翰的游戏(博弈论)(Anti-SG)

解析

我的做法:打表,哦…过了

打表观察的结论:只要不全是1,答案和正常Nim游戏相同,全是1简单讨论奇偶性即可。

证明:
全是1的正确性显然,现在考虑不全是1的时候为什么直接看异或和就行。

关键性质:当仅有一堆大于1的时候必胜。

因为我可以随意的调控接下来权值为1的个数的奇偶性。

同时,只要一开始不全是1,无论如何拿,这个状态都是必然要经历的。
因此,我们可以把它作为一个获胜的终止状态。
此时显然异或和不为0。
那么就和传统Nim的证明一样了,胜方只需要一直保证到自己走时异或和不为0即可。

bonus

很有启发性的一点是,由于Nim游戏和SG函数本质的相通性,这个结论可以推广到所有类似的Anti-SG游戏中。(Anti-SG游戏的定义:决策集合为空的状态视为胜利状态。)

把SG函数当成石子数即可。
(这个玩意好像叫 Sprague Grundy - Jia Zhihao 定理)

代码

(附打表程序)

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
#define debug(...) fprintf(stderr,__VA_ARGS__)
#define ok debug("OK\n")
using namespace std;

const int N=2e5+100;
const int mod=1e9+9;
inline ll read(){
  ll x(0),f(1);char c=getchar();
  while(!isdigit(c)) {if(c=='-')f=-1;c=getchar();}
  while(isdigit(c)) {x=(x<<1)+(x<<3)+c-'0';c=getchar();}
  return x*f;
}


inline ll ksm(ll x,ll k){
  ll res(1);
  while(k){
    if(k&1) res=res*x%mod;
    x=x*x%mod;
    k>>=1;
  }
  return res;
}
int n,m;
int a[20];
int bas=31;
inline ull Hash(int *a,int n){
  ull res=0;
  for(int i=1;i<=n;i++) res=res*bas+a[i];
  return res;
}
map<ull,int>mp;
bool cmp(int x,int y){return x>y;}
int find(const int *x,int n){
  int a[20];
  memcpy(a,x,sizeof(int)*(n+1));
  sort(a+1,a+1+n,cmp);
  while(!a[n]&&n) --n;
  if(n==0) return 1;
  ull h=Hash(a,n);
  if(mp.count(h)) return mp[h];
  int res=0;
  int b[20];
  memcpy(b,a,sizeof(int)*(n+1));
  for(int i=1;i<=n;i++){
    for(int j=0;j<a[i];j++){
      b[i]=j;
      res|=find(b,n)==0;
      b[i]=a[i];
    }
  }
  return mp[h]=res;
}
int op;
int now[20];
inline int calc(int *a,int n){
  int res(0);
  for(int i=1;i<=n;i++) res^=a[i];
  return res;
}
void dfs(int k){
  if(k>m){
    int op=find(now,m);
    if(op==0&&calc(now,m)){
      for(int i=1;i<=m;i++) printf("%d ",now[i]);
      printf("(%d) op=%d",calc(now,m),op);
      puts("");
    }
    if(op==1&&calc(now,m)==0){
      for(int i=1;i<=m;i++) printf("%d ",now[i]);
      printf("(%d) op=%d",calc(now,m),op);
      puts("");
    }
    return;
  }
  for(int i=now[k-1];i<=n;i++){
    now[k]=i;
    dfs(k+1);
  }
  return;
}
void work(){
  n=read();
  int res(0),flag=1;
  for(int i=1;i<=n;i++){
    int x=read();
    res^=x;
    flag&=(x==1);
  }
  if((res==0)^flag) puts("Brother");
  else puts("John");
}

signed main(){
  #ifndef ONLINE_JUDGE
  freopen("a.in","r",stdin);
  freopen("a.out","w",stdout);
  #endif
  //n=read();int lim=read();
  //now[0]=1;
  //for(m=1;m<=lim;m++) dfs(1);
  //return 0;
  int T=read();
  while(T--) work();
  return 0;
}
//288 125 189 111 229 


  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值