题意:
给定 n n n个石子堆,每堆有 a i a_i ai个石子( a i > 0 a_i>0 ai>0),每次可以选择一堆石子,从这堆石子中选择任意多的石子。两者皆采取最优策略,问什么状态下先手必败,什么状态下先手必胜。
题解:
经典尼姆游戏。
当 a 1 ⊕ a 2 ⊕ a 3 ⊕ . . . ⊕ a n − 1 ⊕ a n = 0 a_1\oplus a_2\oplus a_3\oplus ...\oplus a_{n-1}\oplus a_n=0 a1⊕a2⊕a3⊕...⊕an−1⊕an=0,先手必败。
当 a 1 ⊕ a 2 ⊕ a 3 ⊕ . . . ⊕ a n − 1 ⊕ a n ≠ 0 a_1\oplus a_2\oplus a_3\oplus ...\oplus a_{n-1}\oplus a_n\neq 0 a1⊕a2⊕a3⊕...⊕an−1⊕an=0,先手必胜。
证明:定义 s u m = a 1 ⊕ a 2 ⊕ a 3 ⊕ . . . ⊕ a n − 1 ⊕ a n sum=a_1\oplus a_2\oplus a_3\oplus ...\oplus a_{n-1}\oplus a_n sum=a1⊕a2⊕a3⊕...⊕an−1⊕an。
终态一定是所有石子堆的石子个数都为 0 0 0,即 s u m = 0 sum=0 sum=0时。
这里推测:
- s u m = 0 sum=0 sum=0时,先手必败
- s u m ≠ 0 sum\neq 0 sum=0时,先手必胜
证明:
考虑先手处于必胜态时,此时 s u m ≠ 0 sum\neq 0 sum=0,那么先手必然要拿掉一些石子使得后手面临必败态,即先手拿掉一些石子后使得 s u m = 0 sum=0 sum=0。
这里令被操作的石子堆是第 i i i堆,操作前的剩余 n − 1 n-1 n−1堆石子异或值为 H = s u m ⊕ a i H=sum\oplus a_i H=sum⊕ai,若将第 i i i堆石子数量降至 H H H,则 H ⊕ H = 0 H\oplus H=0 H⊕H=0,此时后手将面临必败态。
问题转化为了寻找一个 a i a_i ai,使得 a i > H a_i>H ai>H。
这里对于 s u m sum sum的二进制最高位 k k k,必然也是一个数 a i a_i ai的二进制最高位,那么选择该 a i a_i ai, H H H必然比 a i a_i ai小,因此必然存在这样一个数使得 a i > H a_i>H ai>H。
证毕。
以上是对于经典尼姆游戏的证明。
那么若限制每次拿石子的个数为指定集合中的任意一个数,如何解决判断必败态和必胜态。
考虑 S G SG SG函数的构造。(具体的 S G SG SG函数介绍省略。)
直接给出结论:
令 s u m = S G ( a 1 ) ⊕ S G ( a 2 ) ⊕ S G ( a 3 ) ⊕ . . . ⊕ S G ( a n − 1 ) ⊕ S G ( a n ) sum=SG(a_1)\oplus SG(a_2)\oplus SG(a_3)\oplus ...\oplus SG(a_{n-1})\oplus SG(a_n) sum=SG(a1)⊕SG(a2)⊕SG(a3)⊕...⊕SG(an−1)⊕SG(an)
这里仍然推测:
- s u m = 0 sum=0 sum=0时,先手必败
- s u m ≠ 0 sum\neq 0 sum=0时,先手必胜
考虑到 S G SG SG函数的定义, S G ( a i ) SG(a_i) SG(ai)可以到达任意小于 S G ( a i ) SG(a_i) SG(ai),即上一个模型,证明与尼姆游戏的普通版本一样。问题转化为了如何求解 S G SG SG函数。
给出一个以 f i b fib fib数列作为可取石子的个数集合的 S G SG SG函数的模板: h d u 1848. hdu1848. hdu1848.
int fib[17] = {
0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987};
int sg(int x) {
if(~f[x]) return f[x];
bool jud[M]; //M指的是需要的最大sg参数
memset(jud, false, sizeof jud);
for(int i = 1; i <= 15 && x >= fib[i]; ++i) jud[sg(x - fib[i])] = true;
for(int i = 0; ; ++i) if(!jud[i]) return f[x] = i;
}