Nim積解法小結


由於某毒瘤出題人 redbag 不得不學習一下這個史詩毒瘤算法。

本文參考了 OwaskiGameTheory 的課件。

定義

我們對於一些二維 \(\mathrm{Nim}\) 游戲(好像更高維也行),可以拆分成兩維單獨的 \(\mathrm{Nim}\) 然后求 \(\mathrm{Nim}\) 積。

定義為

\[x \otimes y = \mathrm{mex}\{(a \otimes b) \oplus (a \otimes y) \oplus (x \otimes b), 0 \le a < x, 0 \le b < y\} \]

其中 \(\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

性質

運算的性質

觀察此表,可以顯然的得出:

\[\begin{aligned} 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\) 仍然是單位元,並且滿足交換律。

不會證明的兩個結論:

\[\begin{aligned} 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\)

  1. 一個 \(\text{Fermat 2-power}\) 與任意小於它的數的 \(\mathrm{Nim}\) 積為一般意義下乘法的積,即 \(a \otimes x = a \times x~(x < a)\)

  2. 一個 \(\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\) 的二進制位拆出來,變成一個個費馬數,然后利用性質處理。

\[2^x \otimes 2^y = (\otimes_{x' \in x} 2^{2^{x'}}) \otimes (\otimes_{y' \in y} 2^{2^{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\) 的情況分開。

\[\begin{aligned} 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)\) 的。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM