公式

E - XOR and Shift 解説 by maroonrk_admin


TL;DR 係数が \(F_2[X]/(X^{2^N-1})\) であるベクトルの集合に対し掃き出しを行えば良い. \(\bmod\) 素べきの履き出し法と同じ要領で行えば良い.

以下,丁寧な解説.

値域が \(0,1\) のときの解き方を考えます.

\(f_i(x)=\sum_{0 \leq j \leq 2^N-1} A_{i,j+1} x^j\) とします.

問題文の操作は,\(f_i(x)\) に任意の \(poly(x)\) をかけたものを足し合わせ,\(x^{2^N}-1\) で割ったものを得る,という風に言い換えることができます. ただし,ここで登場する多項式の係数はすべて \(\bmod 2\) で計算されます.

こうして得られる多項式は,\(\gcd(f_1(x),\cdots,f_M(x),x^{2^N}-1)\) の倍数すべて( \(\bmod (x^{2^N}-1)\))です.

ところで,\((x^{2^N}-1)=(x+1)^{2^N}\) です. よって,上記の \(\gcd\) の計算は,各 \(f_i(x)\)\((x+1)\) で何回割り切れるか,という問題に帰着されます.

これは多項式一個あたり \(O(N 2^N)\) 時間で計算することができます. \(f\)\((x+1)^{2^k}\) で割り切れるか検証し,割れるなら割る,という操作を \(k=N-1,\cdots,0\) に対して行えばよいです. \((x+1)^{2^k}=(x^{2^k}+1)\) なので,割り算は非常に簡単です.

上記の \(\gcd\)\((x+1)^e\) だった場合,答えは \(2^{2^N-e}\) になります.

次に値域が一般のときの解き方を考えます. 各 bit について多項式があり,それらを同時に \(poly(x)\) 倍するという問題です.

これは,ベクトルの集合が与えられ,その線型結合で表せるベクトルの個数を数える問題と解釈できます. 係数が特殊ですが,\(\bmod \) 合成数でのやり方と全く同様に処理できます.

具体的には,まず与えられたベクトルの集合を \(v_1,\cdots,v_M\) とおき,\(v_i=(v_{i,1},\cdots,v_{i,L})\) と書くことにします.(\(L\) は値域の bit 数)

\(v_{i,j}\)\(\bmod 2\) の多項式となっているので,これらすべてについて,\(\gcd(v_{i,j},x^{2^N}-1)\) を求めます. \(\gcd\) はすべて \((x+1)^e\) の形をしているのですが,その \(e\) の最小値を達成する\(v_{i,j}\) に注目し,これを \(v_{r,c}\) とします.

掃き出しの要領で,\(v_r\) の定数倍を \(v_i\) (\(i \neq r\)) に足すことで \(v_{i,c}=0\) となるように調整します. そのためには,\(v_{r,c} \times a + v_{i,c}=0\) となる \(a\) を計算できる必要があります.

\(\gcd(v_{r,c}/(x+1)^e,x^{2^N}-1)=1\) であることから,\(v_{r,c}/(x+1)^e\)\(\bmod (x^{2^N}-1)\) での逆元 \(z\) が存在して,\(a=-v_{i,c}/(x+1)^e \times z\) と計算できます.

\(f\) の逆元を計算するときは,\(f^{2^N}=1 \mod{(x^{2^N}-1)}\) であることを用い,\(f^{-1}=f^{2^N-1} \mod{(x^{2^N}-1)}\) とすればよいです.

この調整が終わったあと,\(c\) bit 目は \((1+x)^e\) の倍数すべてが達成可能という状態になります. そして,\(c\) bit 目以外で達成可能な値の組み合わせを数えるときに,\(v_r\) は無視してよいことがわかります.なぜなら,\(v_r\) にかける係数を \(w_r\) とすると,\(v_r \times (x+1)^{2^N-e}=0\) より \(w_r\)\(\bmod (x+1)^{2^N-e}\) だけが重要とわかり,そして \(w_r \bmod (x+1)^{2^N-e}\)\(c\) bit 目の最終的な値から一意に定まるからです.

以上の操作をそのまま実行すると,計算量は \(O(\min(M,L)(ML+N)N2^N)\) となります. ただしここで,多項式の乗算にはFFTを用いています.

回答例(C++)

投稿日時:
最終更新: