想了想还是给这种 blog 取名叫「笔记」比较好。感觉细品来是个很有诗意的名字(我差点就信了)。
没什么干货,基本上都是瞎写,而且是想到什么写什么,所以可能你看完也学习不到什么。
由于我数学很差,所以可能会写大堆废话 + 大堆错误 + 大堆不带证明的结论。
xor
为什么 nim 游戏会与 xor 扯上关系?(虽然就 “验证正确性” 而言这是显然归纳法可证的。。。)
灵感来自于知乎里面的这篇问答。由于知乎的数学公式排版有点难受,所以就自己再梳理了一遍。
首先,我们重新定义 xor 为域 (mathrm{Z/2Z}) 上的线性空间的向量加。相信聪明的读者读到这里已经恍然大悟了,剩下的留作习题。
以下记 (f(a_1,a_2dots a_k)) 表示当前 nim 游戏的 np 状态,其中 (a_i) 是第 (i) 堆石子的状态。
现在我们想找到 (1) 堆石子 (b) 等效替代 ({a_i}),那么有 (f(b)=f(a_1,a_2dots a_k)),由此简化了问题。
为了处理的方便,我们定义 ({a_i}) 与 ({b_i}) 等效当且仅当 (forall c,f(a_1,a_2dots a_p,c) = f(b_1,b_2,dots,b_q,c)),容易验证这是等价关系。
在等价类之间定义运算 (Aoplus B = C),表示把等价类 (A) 与等价类 (B) 组合得到等价类 (C)。这个运算满足:
(1)交换律 (Aoplus B = Boplus A),显然该运算并没有顺序要求。
(2)结合律 ((Aoplus B)oplus C = Aoplus(Boplus C)),由 “等效” 的定义可知。
(3)幺元 (0oplus A = A),显然,因为玩家无法操作没有石子的石堆。
(4)逆元 (Aoplus A = 0),由 nim 游戏的对称性策略可得。
也即它是阿贝尔群。事实上,最后一点还说明该群所有元素(除了幺元)都是二阶元素,因此该群同构于一列二阶群的直积。
然后,你发现 xor 也同构于一列二阶群的直积。。。
然后我实在编不下去了,所以暂时留坑吧(
nimber
草nimber这个名字(nim+number)是哪个人才想到的。
定义域 (F = (N,oplus,otimes)),其中 (oplus) 被称为 nim 和,(otimes) 被称为 nim 积。
(x oplus y) 递归定义为 (mathrm{mex}({xoplus b|b<y}cup{aoplus y|a<x}));
(xotimes y) 递归定义为 (mathrm{mex}({(xotimes b)oplus(aotimes y)oplus(aotimes b) |a<x,b<y}))。
nim 和的意义是直观的,nim 积的意义。。。可以参考 hdu - 3404。
至于它为什么是域。。。
nim 和的快速求解方法就是 xor,在上面已经有讨论过了。
考虑 nim 积怎么快速求解,先甩几个我也不会证的结论:
(1)(2^{2^n}otimes a = 2^{2^n} imes a(a < 2^{2^n}));
(2)(2^{2^n}otimes 2^{2^n} = frac{3}{2} imes2^{2^n});
(3)(aotimes b < 2^{2^n} (a < 2^{2^n},b<2^{2^n}))。
最后一条实际上保证了在 (<2^{2^n}) 时 (F'(S,oplus,otimes)) 形成子域,此时 nim 积逆元可以直接由群论中的拉格朗日定理得到。
如果记 (a=M imes p_a+r_a,b=M imes p_b+ r_b),其中 (p_a,p_b,r_a,r_b<M=2^{2^n})。注意到这里的 ( imes,+) 与 (otimes,oplus) 结果一致。
为了公式的美观,下文中的 (otimes,oplus) 全部写成 ( imes,+)。原本的 ( imes) 省略不写。
[egin{aligned} aotimes b &= (M imes p_a+r_a) imes (M imes p_b+ r_b) \ &=(frac{3}{2}M) imes p_a imes p_b + M(p_a imes r_b+p_b imes r_a)+ r_a imes r_b \ &=(M+ (frac{M}{2})) imes p_a imes p_b + M((p_a + r_a) imes (p_b + r_b) + p_a imes p_b + r_a imes r_b)+r_a imes r_b \ &= (frac{M}{2}) imes(p_a imes p_b)+M((p_a + p_b) imes (r_a + r_b) + r_a imes r_b)+r_a imes r_b end{aligned} ]中间部分用了分治乘法的技巧,注意 (oplus) 的逆运算就是它自己。
由于 (p_a,p_b,r_a,r_b<M=2^{2^n}),所以可以递归到更小的子问题。可以部分记忆化一下优化常数。
时间复杂度 (T(n)=4T(n-1)=4^n),而 (n=O(loglog a)),所以 (T(a) = O(log^2 a))。
typedef unsigned long long ull;
struct nimber{
ull x; nimber() {} nimber(ull _x) : x(_x) {}
friend nimber operator + (const nimber &a, const nimber &b) {
return nimber(a.x ^ b.x);
}
friend nimber mul(const nimber &a, const nimber &b, int L) {
if( a.x <= 1 || b.x <= 1 ) return a.x * b.x;
ull M = (1ull << L);
nimber pa = (a.x >> L), ra = (a.x & (M - 1));
nimber pb = (b.x >> L), rb = (b.x & (M - 1));
if( pa.x == 0 && pb.x == 0 ) return mul(a, b, L >> 1);
nimber p = mul(pa, pb, L >> 1), q = mul(pa + ra, pb + rb, L >> 1), r = mul(ra, rb, L >> 1);
return mul(M >> 1, p, L >> 1) + nimber(M * (q + r).x) + r;
}
friend nimber operator * (const nimber &a, const nimber &b) {
return mul(a, b, 32);
}
friend nimber mpow(nimber a, ull p) {
nimber r; for(r = 1; p; p >>= 1, a = a * a)
if( p & 1 ) r = r * a;
return r;
}
friend nimber operator / (const nimber &a, const nimber &b) {
return a * mpow(b, (ull)-2);
}
};
最后放一张 wiki 上关于 nim 积的图片吧。数学真美啊.jpg。
surreal number(咕)
等我 noi2020 退役完就来补坑。
fibonacci(咕)
看到 snoi2020 有一道这样的题,于是就给自己挖了个这样一个坑。
所以同上,等我 noi2020 退役完就来补坑。