Nim积解法小结
由于某毒瘤出题人 redbag 不得不学习一下这个史诗毒瘤算法。
本文参考了 Owaski 的 GameTheory 的课件。
定义
我们对于一些二维 \(\mathrm{Nim}\) 游戏(好像更高维也行),可以拆分成两维单独的 \(\mathrm{Nim}\) 然后求 \(\mathrm{Nim}\) 积。
定义为
\]
其中 \(\otimes\) 定义为 \(\mathrm{Nim}\) 积,\(\oplus\) 定义为异或。
以下是对于 \(x, y \le 4\) 的一个小表。
| 0 | 1 | 2 | 3 | 4 | |
|---|---|---|---|---|---|
| 0 | 0 | 0 | 0 | 0 | 0 |
| 1 | 0 | 1 | 2 | 3 | 4 |
| 2 | 0 | 2 | 3 | 1 | 8 |
| 3 | 0 | 3 | 1 | 2 | 12 |
| 4 | 0 | 4 | 8 | 12 | 6 |
性质
运算的性质
观察此表,可以显然的得出:
x \otimes 0 &= 0 \otimes x = 0\\
x \otimes 1 &= 1 \otimes x = x\\
x \otimes y &= y \otimes x
\end{aligned}
\]
即 \(0\) 与所有数做 \(\mathrm{Nim}\) 积仍然为 \(0\) , \(1\) 仍然是单位元,并且满足交换律。
不会证明的两个结论:
x \otimes (y \otimes z) &= (x \otimes y) \otimes z\\
x \otimes (y \oplus z) &= (o \otimes y) \oplus (x \otimes z)
\end{aligned}
\]
就是说满足乘法交换律,和乘法分配率(把 \(\otimes\) 看作 \(\times\) 以及 \(\oplus\) 看做 \(+\) )。
费马数的一些运算性质
经过数学家的艰苦卓绝的努力,我们有两个十分强大的运算法则。
定义 \(\text{Fermat 2-power}\) 为 \(2^{2^n}\) ,其中 \(n \in \mathbb N\) ,设其为 \(a\) 。
一个 \(\text{Fermat 2-power}\) 与任意小于它的数的 \(\mathrm{Nim}\) 积为一般意义下乘法的积,即 \(a \otimes x = a \times x~(x < a)\) 。
一个 \(\text{Fermat 2-power}\) 与自己的 \(\mathrm{Nim}\) 积为自己的 \(\displaystyle \frac 32\) 倍,即 \(\displaystyle a \otimes a = \frac 3 2 a = a \oplus \frac a 2\) 。
算法解决
注意暴力求 \(\mathrm{Nim}\) 积是 \(\mathcal O((xy)^2)\) 的,我们可以利用一些性质在 \(\mathcal O(\log x \log y)\) 的时间内解决。
对于任意 \(x, y\) 的解法
我们设 \(f(x, y) = x \otimes y\) ,我们特判 \(x \text{ or } y = 0, 1\) 的情况后,可以考虑拆出 \(x, y\) 的每个二进制位单独算。
就是设 \(g(x, y) = 2^x \otimes 2^y\) ,那么 \(f(x, y) = \oplus_{x' \in x, y' \in y} g(x', y')\) 。
对于 \(2^x \otimes 2^y\) 的解法
这一段是 zhou888 教我的,太恐怖啦 %%%
那么我们问题就转化为求 \(g(x, y)\) 了。
我们考虑把 \(x, y\) 的二进制位拆出来,变成一个个费马数,然后利用性质处理。
\]
考虑 从高到低 依次考虑 \(x, y\) 的每一位,如果这位都为 \(0\) 我们显然可以忽略。
\(x \text{ and } y\) 的情况
假设全都为 \(1\) 那么对于这一位 \(2^u\) 我们设 \(M = 2^{2^u}, A = 2^{x - 2^u}, B = 2^{y - 2^u}\) ,那么有 \(A, B < M\) 。
那么我们的答案其实就是 \(ans = (M \otimes A) \otimes (M \otimes B)\) (注意费马数的 \(\times\) 和 \(\otimes\) 是一样的)即 $ (M \otimes M) \otimes (A \otimes B)$ ,化简一下答案其实就是 \(\displaystyle \frac{3}{2} M \otimes (A \otimes B)\) 。
那么此时我们把 \(2^x, 2^y\) 都去掉最高的一位 \(u\) 变成 \(A, B\) ,继续向低位递归。
\(x \text{ xor } y\) 的情况
假设一个为 \(1\) 一个为 \(0\) ,同样我们设这位为 \(2^u\) ,假设 \(x\) 此位为 \(1\) ,那么有 \(M = 2^{2^u}, A = 2^{x - 2^u}, B = 2^y\) 。
那么答案的形式为 \(ans = (M \otimes A) \otimes B\) 也就是 \(M \otimes (A \otimes B)\) 。类似的,我们去掉最高位,然后不断向下推。
讨论完上面两种情况,我们可以写一下表达式。
我们显然可以利用交换律把 \(x \text { xor } y\) 和 \(x \text { and } y\) 的情况分开。
2^x \otimes 2^y
&= (\otimes_{i \in \{x \text{ xor } y\}} 2^{2^i}) \oplus (\otimes_{i \in \{x \text{ and } y\}} \frac{3}{2} 2^{2^i})\\
&= (\prod_{i \in \{x \text{ xor } y\}} 2^{2^i}) \otimes (\otimes_{i \in \{x \text{ and } y\}} \frac{3}{2} 2^{2^i})
\end{aligned}
\]
那么对于前者可以直接算,后面利用 \(f\) 递归算就行了。
复杂度不难发现只会遍历两个所有二进制位,也就是单次为 \(\mathcal O(\log^2 x)\) 。
代码实现
网上的那种推导以及实现方式似乎都有些问题,似乎是其中一个费马数的地方没有保证 \(<\) ,小的不会错,大的会有些问题。
所以我参考了 zhou888 的代码实现。
#define Resolve(i, x) for (int u = (x), i = 0; (1ll << i) <= u; ++ i) if (u >> i & 1)
ll f(ll x, ll y);
ll g(int x, int y) {
if (!x || !y) return 1ll << (x | y);
if (~ tab[x][y]) return tab[x][y];
ll res = 1;
Resolve(i, x ^ y) res <<= (1 << i);
Resolve(i, x & y) res = f(res, 3ll << ((1 << i) - 1));
return tab[x][y] = res;
}
ll f(ll x, ll y) {
if (!x || !y) return x | y;
if (x == 1 || y == 1) return max(x, y);
ll res = 0;
Resolve(i, x) Resolve(j, y) res ^= g(i, j);
return res;
}
例题
HDU3404 Switch lights
题意
在一个二维平面中,有 \(n\) 个灯亮着并告诉你坐标,每回合需要找到一个矩形,这个矩形 \((x,y)\) 坐标最大的那个角落的点必须是亮着的灯,然后我们把四个角落的灯状态反转,不能操作为败。
\(T \le 100, n \le 1000, x, y \le 10000\)
题解
\(\mathrm{Turning~Corners}\) 是裸的二维 \(\mathrm{Nim}\) 问题,直接上模板就好了。
复杂度是 \(\mathcal O(Tn\log x \log y)\) 的。
Nim积解法小结的更多相关文章
- HDU 3404&POJ 3533 Nim积(二维&三维)
(Nim积相关资料来自论文曹钦翔<从"k倍动态减法游戏"出发探究一类组合游戏问题>) 关于Nim积计算的两个函数流程: 代码实现如下: ][]={,,,}; int N ...
- POJ 3533 Light Switching Game(三维Nim积)题解
思路:三维Nim积 代码: #include<set> #include<map> #include<stack> #include<cmath> #i ...
- HDU 3404 Switch lights(Nim积)题解
题意:在一个二维平面中,有n个灯亮着并告诉你坐标,每回合需要找到一个矩形,这个矩形xy坐标最大的那个角落的点必须是亮着的灯,然后我们把四个角落的灯状态反转,不能操作为败 思路:二维Nim积,看不懂啊, ...
- POJ 3553 Light Switching Game 博弈论 nim积 sg函数
http://poj.org/problem?id=3533 变成三维的nim积..前面hdu那个算二维nim积的题的函数都不用改,多nim积一次就过了...longlong似乎不必要但是还是加上了 ...
- HDU 3404 Switch lights 博弈论 nim积
http://acm.hdu.edu.cn/showproblem.php?pid=3404 题目 http://www.doc88.com/p-5098170314707.html 论文 nim积在 ...
- Nim积的一种???的写法
Nim积总不能一直打四次暴力吧! 用SG定理等东西,可以证明 \((N, \oplus, \otimes)\) 构成一个域.(证明很难,我不会) 其中 \(\oplus\) 为异或, \(x \oti ...
- 2-SAT 问题与解法小结
2-SAT 问题与解法小结 这个算法十分的奇妙qwq... 将一类判定问题转换为图论问题,然后就很容易解决了. 本文有一些地方摘录了一下赵爽<2-SAT解法浅析> (侵删) 一些概念: \ ...
- Nim积
假如把Nim游戏的取胜规则改为谁取走最后一个石子谁输的话 先手必胜当且仅当: 1.所有堆的石子数都为1且游戏的SG值为0 2.有些堆的石子数大于1且游戏的SG值不为0
- nim游戏解法(转)
转自:http://acm.hdu.edu.cn/forum/read.php?fid=9&tid=10617 取火柴的游戏 题目1:今有若干堆火柴,两人依次从中拿取,规定每次只能从一堆中取若 ...
随机推荐
- linux c ---raise 使用范例的代码
把做工程过程中比较好的代码片段收藏起来,下面代码内容是关于linux c ---raise 使用范例的代码,希望对各位有所用途. #include <sys/types.h> #inclu ...
- 记一次与iframe之间的抗争
iframe这个标签之前了解过这个东西,知道它可以引入外来的网页,但是实际开发中没有用到过.这一次有一个需求是说准备要在网页中嵌套另外一个网站,用iframe这个标签,让我测试一下这个可不可以在自己的 ...
- 定时任务 Cron表达式
Cron表达式由6~7项组成,中间用空格分开.从左到右依次是: 秒.分.时.日.月.周几.年(可省略) Cron表达式的值可以是数字,也可以是以下符号: "*":所有值都匹配 &q ...
- c/c++ 继承与多态 子类隐藏父类的同名非虚函数
问题1:若类A里有公有成员方法int fcn(),类B公有继承类A,并类B里有公有成员方法int fcn(int),可以在用户代码里,用类B的对象调用类A的int fcn()吗?? 不可以,编译出错, ...
- IOS判断NSArray是否为空
场景描述:判断一个集合是否为空,如果不为空执行A,如果为空执行B Java实现方法 public void exec(){ List<String> list = this.getCont ...
- Windows系统资源监控
1.windows自带系统资源监控工具 2.Windows监控的数据来源:Performance Counters 2.1 Performance Counter架构 2.2 Performance ...
- ansible学习(二)
什么是YAML? YAML是一种标记语言.适合用来表达层次结构式的数据结构. YAML的基本组件:清单(短杠——空白字符)和散列表(短杠+空白字符分隔key:value对). Playbook的核心元 ...
- 周一01.4安装PyCharm步骤
安装集成开发工具 步骤一 步骤二 步骤三 步骤四
- Spring系列-SpringBoot 学习路径
学习spring boot 已经有很长一段时间,与其说学习,不如说是使用. 在过去的很长时间我一直奉行实用主义,任何技术我都是在应用中使用,很少是因为为了学习而学习. 当然,有那么几次心血来潮,去专门 ...
- 事务的ACID属性,图解并发事务带来问题以及事务的隔离级别
事务的概述 事务是指作为单个逻辑工作单元执行的一系列操作,要么完全地执行,要么完全地不执行. 事务处理可以确保除非事务性单元内的所有操作都成功完成,否则不会永久更新面向数据的资源.通过将一组相关操作组 ...