Nim积解法小结

由于某毒瘤出题人 redbag 不得不学习一下这个史诗毒瘤算法。

本文参考了 OwaskiGameTheory 的课件。

定义

我们对于一些二维 Nim 游戏(好像更高维也行),可以拆分成两维单独的 Nim 然后求 Nim 积。

定义为

xy=mex{(ab)(ay)(xb),0a<x,0b<y}

其中 定义为 Nim 积, 定义为异或。


以下是对于 x,y4 的一个小表。

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

性质

运算的性质

观察此表,可以显然的得出:

x0=0x=0x1=1x=xxy=yx

0 与所有数做 Nim 积仍然为 01 仍然是单位元,并且满足交换律。

不会证明的两个结论:

x(yz)=(xy)zx(yz)=(oy)(xz)

就是说满足乘法交换律,和乘法分配率(把 看作 × 以及 看做 + )。

费马数的一些运算性质

经过数学家的艰苦卓绝的努力,我们有两个十分强大的运算法则。

定义 Fermat 2-power22n ,其中 nN ,设其为 a

  1. 一个 Fermat 2-power 与任意小于它的数的 Nim 积为一般意义下乘法的积,即 ax=a×x (x<a)

  2. 一个 Fermat 2-power 与自己的 Nim 积为自己的 32 倍,即 aa=32a=aa2

算法解决

注意暴力求 Nim 积是 O((xy)2) 的,我们可以利用一些性质在 O(logxlogy) 的时间内解决。

对于任意 x,y 的解法

我们设 f(x,y)=xy ,我们特判 x or y=0,1 的情况后,可以考虑拆出 x,y 的每个二进制位单独算。

就是设 g(x,y)=2x2y ,那么 f(x,y)=xx,yyg(x,y)

对于 2x2y 的解法

这一段是 zhou888 教我的,太恐怖啦 %%%

那么我们问题就转化为求 g(x,y) 了。

我们考虑把 x,y 的二进制位拆出来,变成一个个费马数,然后利用性质处理。

2x2y=(xx22x)(yy22y)

考虑 从高到低 依次考虑 x,y 的每一位,如果这位都为 0 我们显然可以忽略。

x and y 的情况

假设全都为 1 那么对于这一位 2u 我们设 M=22u,A=2x2u,B=2y2u ,那么有 A,B<M

那么我们的答案其实就是 ans=(MA)(MB) (注意费马数的 × 是一样的)即 (MM)(AB) ,化简一下答案其实就是 32M(AB)

那么此时我们把 2x,2y 都去掉最高的一位 u 变成 A,B ,继续向低位递归。

x xor y 的情况

假设一个为 1 一个为 0 ,同样我们设这位为 2u ,假设 x 此位为 1 ,那么有 M=22u,A=2x2u,B=2y

那么答案的形式为 ans=(MA)B 也就是 M(AB) 。类似的,我们去掉最高位,然后不断向下推。


讨论完上面两种情况,我们可以写一下表达式。

我们显然可以利用交换律把 x xor yx and y 的情况分开。

2x2y=(i{x xor y}22i)(i{x and y}3222i)=(i{x xor y}22i)(i{x and y}3222i)

那么对于前者可以直接算,后面利用 f 递归算就行了。

复杂度不难发现只会遍历两个所有二进制位,也就是单次为 O(log2x)

代码实现

网上的那种推导以及实现方式似乎都有些问题,似乎是其中一个费马数的地方没有保证 < ,小的不会错,大的会有些问题。

所以我参考了 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) 坐标最大的那个角落的点必须是亮着的灯,然后我们把四个角落的灯状态反转,不能操作为败。

T100,n1000,x,y10000

题解

Turning Corners 是裸的二维 Nim 问题,直接上模板就好了。

复杂度是 O(Tnlogxlogy) 的。


__EOF__

本文作者zjp_shadow
本文链接https://www.cnblogs.com/zjp-shadow/p/10507030.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   zjp_shadow  阅读(3473)  评论(22编辑  收藏  举报
编辑推荐:
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
点击右上角即可分享
微信分享提示