[关闭]
@ysner 2018-08-13T08:04:10.000000Z 字数 1305 阅读 2031

[HNOI2007]分裂游戏

博弈论


题面

堆石子,每次可取第堆石子的一颗,然后再各在堆石子中放一颗()。无法操作者输。问先者是否有必胜策略,和在胜利前提下,第一步有多少种取法。

解析

显然游戏结束的条件是只有最后一堆有石子。
若先者要赢,首先应把各堆石子全部变为偶数,这样只用模仿对手操作即可胜利。
于是记忆化搜索计算每堆石子的值(后继状态是第堆和第k$堆石子)。

注意到目的,我们只统计值异或和,得到
然后可以枚举取的是哪堆石子,若取完后能使当前局面异或和清(即),就是合法方案的一种。

注意一下判的小技巧。

  1. // luogu-judger-enable-o2
  2. #include<iostream>
  3. #include<cstdio>
  4. #include<cstdlib>
  5. #include<cstring>
  6. #include<cmath>
  7. #include<algorithm>
  8. #define ll long long
  9. #define re register
  10. #define il inline
  11. #define fp(i,a,b) for(re int i=a;i<=b;i++)
  12. #define fq(i,a,b) for(re int i=a;i>=b;i--)
  13. using namespace std;
  14. const int N=50;
  15. int n,a[N],SG[N],vis[N],ans,tot;
  16. il ll gi()
  17. {
  18. re ll x=0,t=1;
  19. re char ch=getchar();
  20. while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
  21. if(ch=='-') t=-1,ch=getchar();
  22. while(ch>='0'&&ch<='9') x=x*10+ch-48,ch=getchar();
  23. return x*t;
  24. }
  25. il int dfs(re int x)
  26. {
  27. if(SG[x]!=-1) return SG[x];
  28. fp(i,x+1,n)
  29. fp(j,i,n)
  30. vis[dfs(i)^dfs(j)]=x;
  31. re int p=0;
  32. while(vis[p]==x) ++p;
  33. return SG[x]=p;
  34. }
  35. int main()
  36. {
  37. re int T=gi();
  38. while(T--)
  39. {
  40. memset(SG,-1,sizeof(SG));memset(vis,0,sizeof(vis));ans=0;tot=0;
  41. n=gi();
  42. fp(i,1,n) a[i]=gi();
  43. fp(i,1,n) if(a[i]&1) dfs(i);
  44. fp(i,1,n) if(a[i]&1) ans^=dfs(i);
  45. fp(i,1,n)
  46. fp(j,i+1,n)
  47. fp(k,j,n)
  48. if((ans^dfs(i)^dfs(j)^dfs(k))==0)
  49. {
  50. if(!tot) printf("%d %d %d\n",i-1,j-1,k-1);
  51. ++tot;
  52. }
  53. if(!tot) puts("-1 -1 -1");
  54. printf("%d\n",tot);
  55. }
  56. return 0;
  57. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注