Portal -->bzoj3567

Solution

​   今天开始啃博弈论了qwq

​   先mark一篇很棒的博客Portal -->博弈论学习资料

​​   稍微总结一下两个自己容易混淆的点:

1.有一类博弈论问题的主要步骤是首先将原游戏拆分成若干个独立的子游戏,然后原游戏的\(sg\)就是子游戏\(sg\)值的异或和

2.有向图游戏中,对于一个局面,它的\(sg\)是其后继局面的\(sg\)值的\(mex\)

​  

​​   这题的话,首先想怎么拆分

​​   不难发现每一堆的操作其实是独立的,所以我们可以将对一堆石子进行操作看成这个游戏的子游戏

​   那么问题就变成了要求一堆石子数量为\(x\)的石子堆的\(sg\)值,我们考虑用这堆石子的数量来描述这个局面,那么也就是说我们现在要求\(sg(x)\)

​   这其实相当于一个有向图游戏

​​   先不考虑时间和空间问题,我们想如何暴力求解\(sg(x)\),一个可行的方法就是记忆化搜索,对于当前局面\(x\),我们枚举一个\(i\)表示将这\(x\)个石子分成的堆数,那么这个后继局面的\(sg\)值就应该是\(sg(a_1)\wedge sg(a_2)\wedge sg(a_3)\wedge...\wedge sg(a_i)\),其中\(a\)表示的是分成\(i\)堆之后每堆的石子数量,然后\(sg(x)\)应该是所有的后继局面的\(sg\)值的\(mex\)

​​   弄清楚这点之后,我们考虑分成\(i\)堆应该怎么分,根据题目要求,显然我们只能分成\(x\%i\)堆石子数量为\(\lfloor \frac{x}{i}\rfloor+1\)的,和\(i-x\%i\)堆石子数量为\(\lfloor \frac{x}{i}\rfloor\)的,那么根据异或的性质不难得出结论分成\(i\)堆的后继局面的\(sg\)值为:

\[(x\% i=奇数?sg(\lfloor\frac{x}{i}\rfloor+1):0)\wedge(i-x\%i=奇数?sg(\lfloor\frac{x}{i}\rfloor):0)
\]

​​   具体的话就是因为如果是偶数那一部分全部异或起来就是\(0\)了

​  

​​   那么现在我们可以写出来一个暴力,考虑如何优化这个东西

​​   注意到里面有一个\(\lfloor \frac{x}{i}\rfloor\),然后这个东西只有\(\sqrt x\)种取值,处理这种东西我们可以用一个分段的套路(莫比乌斯反演既视感qwq),把所有\(\lfloor \frac{x}{i}\rfloor\)相同的\(i\)一起算,接下来在\(\lfloor \frac{x}{i}\rfloor\)相同的前提下,我们再考虑一下奇偶性的问题:会发现如果说\(i\)的奇偶性不变的话,那么\(x%i\)和\(i-x\%i\)的奇偶性也不会发生变化,又因为\(\lfloor \frac{x}{i}\rfloor\)相同,也就是说\(i\)奇偶性不变的话\(sg\)值也不会发生变化

​​   那么所以,我们只要对于每一段\(\lfloor \frac{x}{i}\rfloor\)相同的\(i\),算一下\(i\)的\(sg\)再算一下\(i+1\)的\(sg\),就能够代表这里所有的情况了,总共是\(2\sqrt x\)个数,记忆化搜索一波,问题不大

​​   最后是一些实现上的小细节,如果说求\(mex\)是暴力求的话,我们不能够在递归的时候每次都将判断的数组重置,所以考虑用一个打标记的方式来判,具体就是每次赋成一个不同的\(mark\)值就好了,但是这里有一个问题就是一旦采取这样的方式,在开始统计之后就不能再进行递归操作,所以我们应该在统计之前先递归一遍把这\(2\sqrt x\)个\(sg\)算出来,要注意因为中间可能会递归到自己,所以一开始要先把\(x\)的\(sg\)值赋成\(0\),防止。。一直递归下去qwq

​​   当然啦如果你写的是递推版本就没有那么多麻烦事了qwq

​  

​​   代码大概长这个样子

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N=100010;
int f[N];
int vis[N];
int n,m,T,F,ans,mark;
int sg(int x){
if (f[x]!=-1) return f[x];
int tmp1,tmp2,tmp=0;
f[x]=0;//stop first
for (int i=2,pos=0;i<=x;i=pos+1){
pos=x/(x/i);
sg(x/i),sg(x/i+1);
}
++mark;
for (int i=2,pos=0;i<=x;i=pos+1){
pos=x/(x/i);
tmp=0;
tmp1=i-x%i;
tmp2=x%i;
if (tmp1&1) tmp^=f[x/i];
if (tmp2&1) tmp^=f[x/i+1];
vis[tmp]=mark; if (x/(i+1)==x/i){
tmp=0;
tmp1=(i+1)-x%(i+1);
tmp2=x%(i+1);
if (tmp1&1) tmp^=f[x/(i+1)];
if (tmp2&1) tmp^=f[x/(i+1)+1];
vis[tmp]=mark;
}
}
for (int i=0;i<=x;++i)
if (vis[i]!=mark){f[x]=i;return f[x];}
} int main(){
#ifndef ONLINE_JUDGE
freopen("a.in","r",stdin);
#endif
int x;
scanf("%d%d",&T,&F);
for (int i=F;i<N;++i) f[i]=-1;
for (int o=1;o<=T;++o){
scanf("%d",&n);
ans=0;
for (int i=1;i<=n;++i){
scanf("%d",&x);
ans^=sg(x);
}
if (ans==0) printf("%d ",0);
else printf("%d ",1);
}
}

最新文章

  1. Hive On Spark环境搭建
  2. canvas练习
  3. JavaWeb(李兴华著)开发笔记
  4. .net C# 对虚拟目录IIS的操作
  5. mysql 5.7 zip 文件在 windows下的安装
  6. Visible 绑定
  7. [mysql] mysqldump 导出数据库表
  8. 关于overflow-y:scroll ios设备不流畅的问题
  9. 网易游戏QA工程师笔试回忆-2012.9【个人题解】
  10. hdu 3018
  11. 链表list容器中通过splice合并链表与merge的不同,及需要注意的问题
  12. app 评分
  13. Sql的连接表补充
  14. LCA倍增算法
  15. Java:现有线程T1/T2/T3,如何确保T1执行完成之后执行T2,T3在T2执行完成之后执行。
  16. Ext.Net 1.X_读写配置文件
  17. Redis笔记-集群搭建
  18. mysql5.6.x 字符集修改
  19. sklearn导入模块问题:python ImportError: No module named datasets
  20. [matlab] 2.数据可视化

热门文章

  1. CTF--zip伪加密
  2. html5shiv 是一个针对 IE 浏览器的 HTML5 JavaScript 补丁,目的是让 IE 识别并支持 HTML5 元素。
  3. object-fix/object-position
  4. 好用的MarkDown编辑器
  5. kaldi DNN在线解码 aishell为例
  6. Kafka安装之三 spring-kafka实践
  7. curl常用用法
  8. netty初认识
  9. 过山车 HDU 2063 (二分图匹配裸题)
  10. IDEA + SSH OA 第一天(项目收获:Hibernate XML)