因爲某毒瘤出題人
redbag不得不學習一下這個史詩毒瘤算法。算法本文參考了 Owaski 的 GameTheory 的課件。學習
咱們對於一些二維 \(\mathrm{Nim}\) 遊戲(好像更高維也行),能夠拆分紅兩維單獨的 \(\mathrm{Nim}\) 而後求 \(\mathrm{Nim}\) 積。spa
定義爲code
\[ 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\) 的一個小表。get
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} \]it
即 \(0\) 與全部數作 \(\mathrm{Nim}\) 積仍然爲 \(0\) , \(1\) 仍然是單位元,而且知足交換律。table
不會證實的兩個結論:
\[ \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\) 。
一個 \(\text{Fermat 2-power}\) 與任意小於它的數的 \(\mathrm{Nim}\) 積爲通常意義下乘法的積,即 \(a \otimes x = a \times x~(x < a)\) 。
一個 \(\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)\) 的時間內解決。
咱們設 \(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')\) 。
這一段是 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\) 咱們顯然能夠忽略。
假設全都爲 \(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\) ,繼續向低位遞歸。
假設一個爲 \(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; }
在一個二維平面中,有 \(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)\) 的。