博弈论乱写2:Nimber
需要一点代数能力,应该不高。
Nimber
在博弈论的学习过程中,我们发现了 Nim 游戏的重要性,实际上,所有 ICG 游戏都等价于若干尼姆游戏的和。
我们假设只有一堆的尼姆游戏的值为 \(x\),那么两个尼姆游戏的和就等于 \(x\operatorname{xor}y\)。这个性质不难推广到多个尼姆游戏间,我们把这个描述了游戏必胜状态的值叫做 Nimber,或者尼姆数。
Nim 和
定义 \(a\oplus b\) 表示大小为 \(a\) 和 \(b\) 的两个 Nim 游戏的和,定义如下:
\(a\oplus b=\operatorname{mex}(\{a\oplus b^\prime\mid 0\le b^\prime<b\}\cup\{a^\prime\oplus b\mid 0\le a^\prime<a\})\)。
实际上,我们知道 \(a\oplus b=a\operatorname{xor}b\)。(虽然 \(\operatorname{mex}\) 不是唯一的合法运算,但是用 \(\operatorname{mex}\) 时,\(\oplus\) 和 \(\operatorname{xor}\) 是等价的)
Nim 积
Nim 积的定义
既然 Nimber 的加法和自然数的加法满足许多共性,比如都满足交换律、结合律,我们期望 \(\oplus\) 运算可以这有对应的乘法 \(\otimes\),使得 \(\oplus\) 关于 \(\otimes\) 有分配律,这样 Nimebr 和 \((\oplus,\otimes)\) 就可以构成一个域。
在多项式的学习中,我们都知道,\(\operatorname{deg}(F+G)=\max(\operatorname{deg}(F),\text{deg}(G)),\operatorname{deg}(FG)=\operatorname{deg}(F)+\operatorname{deg}(G)\)。也就是说,一般的乘法对应了维度变化的操作,我们期望 \(\otimes\) 也满足这个性质。
于是引出了 \(k\) 维 Nim 游戏:
在一个 \(k\) 维空间上,有若干个黑点,\((a_{1,i},a_{2,i}\ldots a_{k,i})\),每次选择一个 \((b_1,b_2\ldots b_k),(a_{1,i},a_{2,i}\ldots a_{k,i})\),满足 \(0\le b_1< a_{1,i},0\le b_2<a_{2,i}\cdots 0\le b_k<a_{k,i}\)。将 \(k\) 维立方体 \((b_1,b_2\ldots b_k)-(a_{1,i},a_{2,i}\ldots a_{k,i})\) 的所有端点反色。比如矩形有 \(4\) 个端点,立方体有 \(8\) 个端点,以此类推,\(k\) 维立方体有 \(2^k\) 个端点。
显然,\(k=1\) 就是取石子游戏的模型。
和多项式乘法不同,高维 Nim 游戏十分困难,我们希望 \(\otimes\) 对应的是降维的操作。
直接给出 \(k=2\) 时的定义:
发现结果显然是 \(1\) 维的,于是可以把 \(k\) 个维度依次乘起来得到最后的结果。上式可以理解为关于 \(\operatorname{xor}\) 的容斥。
让人惊喜的是,\(\otimes\) 满足许多优美的性质:
\(x\otimes0=0\)
\(x\otimes1=x\)
\(x\otimes y=y\otimes x\)
\(x\otimes y\otimes z=x\otimes (y\otimes z)\)
\(x\otimes(y\oplus z)=x\otimes y\oplus x\otimes z\),这里我们认为 \(\otimes\) 的优先级高于 \(\oplus\)。
上面的性质用定义不难验证。
不过我们目前还没有和 \(a\oplus b=a\operatorname{xor}b\) 一样简单的方法计算 \(a\otimes b\) 的值。为了方便我们的计算,可以把后文中的 \(\oplus\) 理解为异或运算,但应当记住 \(\oplus\) 表示的是 Nim 和,异或只是计算的方法。
Nim 积的运算
称 \(f_k=2^{2^k}\) 为费马数。由前人的努力,我们知道了:(后面的 \(\times,+\) 就是自然数的对应运算)
\(f_k\) 关于 \(k\) 的增速是很快的,有 \(f_6=2^{64}\),也就是说在实际应用中你几乎见不到 \(k>6\) 的情况。故之后我们只考虑 \(x\in[0,2^{2^k})\) 的 \(x\),这里的 \(k\) 认为是小常数。(严格的说,\(k=O(\log\log V)\))
考虑按位分治,设 \(x=af_{k-1}+b,y=cf_{k-1}+d\)。(\(a,c<f_{k-1}\))
显然有 \(af_{k-1}+b=af_{k-1}\operatorname{xor}b=af_{k-1}\oplus b\),\(c,d\) 同理。
因为 \(a<f_{k-1}\),所以 \(af_{k-1}=a\otimes f_{k-1}\),\(c\) 同理。
再设 \(n=2^{k-1}\),则 \(2^n=f_{k-1}\)。
那么有:
此时只需要计算 \(a\otimes c,a\otimes d,b\otimes c,b\otimes d,(a\otimes c)\otimes 2^{n-1}\) 这 \(5\) 项,可以做到 \(O(5^k)\)。
代码如下:
const u32 map_size=256;
u32 mp[map_size][map_size];
inline u32 nim_mul(u32 x,u32 y)
{
if(x<y) swap(x,y);
if(!y) return 0u;
if(y==1) return x;
if(x<map_size&&mp[x][y]) return mp[x][y];
ull t=2;
u32 len=1;
while(t*t<=x) t=t*t,len<<=1;
u32 a=x>>len,b=x&(t-1),c=y>>len,d=y&(t-1);
u32 c1=nim_mul(a,c);
u32 c2=nim_mul(a,d)^nim_mul(b,c);
u32 c3=nim_mul(b,d);
if(x<map_size) return mp[x][y]= ((c1^c2)*t) ^ c3 ^ nim_mul(t>>1u,c1);
return ((c1^c2)*t) ^ c3 ^ nim_mul(t>>1u,c1);
}
注意 \(\oplus\) 运算的性质,有 \(a\oplus b\oplus b=a\),于是可以进一步优化:
此时,只需要计算 \((a\oplus b)\otimes(c\oplus d),b\otimes d,a\otimes c,(a\otimes c)\otimes 2^{n-1}\) 这 \(4\) 项,复杂度优化到了 \(O(4^k)\)。
代码如下:
const u32 map_size=256;
u32 mp[map_size][map_size];
inline u32 nim_mul(u32 x,u32 y)
{
if(x<y) swap(x,y);
if(!y) return 0u;
if(y==1) return x;
if(x<map_size&&mp[x][y]) return mp[x][y];
ull t=2;
u32 len=1;
while(t*t<=x) t=t*t,len<<=1;
u32 a=x>>len,b=x&(t-1),c=y>>len,d=y&(t-1);
u32 c1=nim_mul(a,c);
u32 c2=nim_mul(a^b,c^d);
u32 c3=nim_mul(b,d);
u32 c4=nim_mul(c1,t>>1);
if(x<map_size) return mp[x][y]= ((c2^c3)*t) ^ c3 ^ c4;
return ((c2^c3)*t) ^ c3 ^ c4;
}
上面的乘法相当于正常乘法时按位模拟,其实我们可以折半,用 \(F\) 最均匀的把 \(x\) 分成 \(x=x_0F+x_1\),同理有 \(y=y_0F+y_1\)。
于是有
过程和之前差不多,不过因为是折半,所以迭代只进行 \(O(\log k)\) 轮,复杂度进一步优化到 \(O(4^{O(\log k)})\),或者说 \(O(\log (V)^{\log_2^3})\)。
还可以用原根进一步优化,打表发现,\(k=4\) 时最小的原根是 \(258\),于是可以把 \(\otimes\) 转化为原根指数上的加法(这里是正常的自然数 \(+\))。
Nim 逆元,开方
我们考虑的 Nimber 在 \([0,f_k)\cap\mathbb{N}\) 中,这是一个有限域,考虑 \(f(x,y)=y\otimes x\),迭代这个函数 \(g(0)=f(x,x),g(k)=f(x,g(k-1))\),\(g\) 一定是有周期的,用一点群论(数论?)知识,这个周期的倍数中一定有 \(f_k\)。
又因为 \(x\otimes 1=x,x^{f_k}=x\),所以 \(x^{f_k-1}=1\),这就是 Nim 求逆。
类似可以定义 \(\sqrt x=x^{2^{2^k-1}}\)。
Nim 多项式
我们当然不可能在指数上用 Nim 数,于是我们在系数上使用 Nim 数。实际上,Nimber 是一个性质很好的数域,我们可以把之前学过的几乎任何东西搬上去。(比如高斯消元,多项式的一堆板子)
给出 Nim 多项式更严格的定义,我们称 \([0,2^{2^k})\cap \mathbb{Z}\) 内的 Nimber 构成的数域为 \(\mathbb{M}\)。
一个 Nim 多项式就是 \(A(x)=a_0\oplus a_1\otimes x\oplus a_2\otimes x^2\oplus\ldots\)。简记为 \(A(x)=\mathbb{M}(a_0,a_1,a_2\ldots)\)。
Nim 多项式的加就是 \(\oplus\),即 \(A(x)\oplus B(x)=A(x)+B(x)=A(x)-B(x)=(a_0\oplus b_0,a_1\oplus b_1,a_2\oplus b_2\ldots)\)。
Nim 多项式的卷积:
Nim 多项式的数乘和一般多项式不同,\(\lambda\times A(x)=(\lambda\times a_0,\lambda\times a_1\ldots)\),我们定义 \(0\times a=0,1\times a=a,\lambda\times a=((\lambda-1)\times a)\oplus a\)。
但是我们发现我们无法定义 EGF,\(\dfrac x2\) 在 \(\mathbb{M}\) 中不一定是一个存在的数。即使考虑 \(e^{A(x)}=B(x)\iff B^\prime(x)=B(x)\otimes A^\prime(x)\) 也不行,因为我们只能微分,不能积分。
一个合理的转化是给前 \(n\) 项的系数乘上 \(n!\),这样一定是整数,也一定在 \(\mathbb{M}\) 上存在,而我们常常只关心前若干项。接着,我们把 EGF 的卷积看做二项卷积,则有
\(\binom ni\) 当然是一个整数,由数乘的定义,我们只关心其奇偶。由 Lucas 定理,我们可以知道 \(\binom{a+b}{a}\equiv 1\pmod 2\) 当且仅当 \(a\operatorname{bitand}b=0\)。就有 \(\binom{n}{i}\times(a_{i+1}\otimes b_{n-i})=[i\operatorname{bitand}(n-i)=0](a_{i+1}\otimes b_{n-i})\),发现这就是子集卷积。
结合半在线卷积的知识,就可以 \(O(n^{\log_23})\) 算 Nim 多项式 \(\exp\) 了。(当然,默认预处理原根后可以 \(O(1)\) 计算 \(a\otimes b\))
这样的复杂度不够 poly,我们期望更快的速度。
我们用 \(A(x)\circ B(x)\) 表示 \(A(x)\) 和 \(B(x)\) 的二项卷积。
普通卷积下,Nim 多项式不能 \(\exp\),但是二项卷积打破了这一屏障,我们期望二项卷积也可以实现求逆,这样我们就可以用 \(\ln\) 和牛顿迭代解决 \(\exp\) 了。
利用 Nim 数的性质 \(x\oplus x=0\),即自己的加法逆元是自己,我们可以得到 \(A(x)\circ A(x)=a_0\otimes a_0\)。这是非常优美的性质,如果有 \(a_0=1\),那么 \(A(x)\circ A(x)=1\),这意味着 \(A(x)\) 的二项卷积逆就是 \(A(x)\)。
在二项卷积的意义下,\(e^{A(x)}=B(x)\iff B^\prime(x)=B(x)\circ A^\prime(x)\),沿用多项式 \(\exp\) 中的假设,我们有 \(a_0=0,b_0=1\)。于是就有 \(B(x)\circ B(x)=1\),那么
我们虽然没有求出 \(\exp\),但我们可以 \(O(n\log^2n)\) 求 \(\ln\) 了,于是还是可以牛迭。
多项式牛顿迭代直接改改就好了:
就得到了 \(O(n\log^2n)\) 的优秀做法。