こどふぉブログ勉強月間では、 codeforces blog の積読を消化して、ついでに日本語の資料を作っておくことを目標にします。
参考資料
- Permutation group basis construction (Schreier–Sims algorithm) codeforces blog, by adamant
- NPCAPC2024 J – Again Permutation Problem
プログラムに実装するときに私は混乱したので、本投稿では、配列の考えで理解しやすいと思う書き方にしました。
対称群の部分群とは
$1$ 以上の整数 $n$ を固定します。 $(0,1,\ldots ,n-1)$ の要素を並べ替えて得られる列全体の集合を $S_n$ とします。 $s\in S_n$ であるとき、 $s$ の $k$ 番目(一番左は $0$ 番目として数える)の要素の値を $s[k]$ で参照します。
$a\in S_n$ , $b\in S_n$ に対して、 $(a[b[0]],a[b[1]],\ldots ,a[b[n-1]])$ を $ab$ と表します。つまり $(ab)[k]=a[b[k]]$ です。この 2 項演算によって、 $S_n$ は群になります:
- $a\in S_n$ , $b\in S_n$ , $c\in S_n$ ならば $a(bc)=(ab)c$ です。
(∵ $(ab)[c[k]]=a[b[c[k]]]=a[(bc)[k]]$) - $e=(0,1,\ldots ,n-1)$ は単位元です。
- $a\in S_n$ に対して、 $a$ の逆元は $a^{-1}[a[k]]=k$ とすればとれます。
$S_n$ の部分集合 $A$ に対して、「 $a\in A$ , $b\in A$ , $ab\notin A$ となるような $a$ , $b$ を選び、 $ab$ を $A$ に加える」という操作を可能な限り繰り返して得られる集合 $A$ を $G$ とすると、これも群をなします。このとき $G=\langle A \rangle$ と表します。
問題
$S_n$ の部分集合 $A$ が与えられます。以下の条件を満たす $G_1,G_2,\ldots ,G_{n-1}$ ( $G_k\subset S_n$ )を求めてください。
- $g\in G_k$ のとき、 $g[x]=x$ ( $k\lt x$ ) 。
- $g\in G_k$ のとき、 $g[k]\neq k$ 。
- $G_k$ の要素 $g$ 全体で、 $g[k]$ の値は相異なる。
- 「$g_1\in G_1,g_2\in G_2,\ldots ,g_{n-1}\in G_{n-1}$ を用いて $g=g_{n-1}\cdots g_2g_1$ と表せる。」 $\iff$ 「 $g\in \langle A \rangle$ 。」
$\langle A \rangle$ の要素全体を、 $n-1$ 番目の要素から順に決定してゆく(*1)手順で表すことで、具体的な計算を DP で効率化することにつながります。全体は、 $O(n^6)$ 時間で計算できます。
(*1) $g[n-1]=g_{n-1}[ \cdots g_2[g_1[n-1]]\cdots ]=g_{n-1}[n-1]$ です。つまり、 $g[n-1]$ の値は $g_{n-1}$ のみに依存します。
A の要素を絞り込む
$A\subset S_n$ が与えられたとき、 $\langle A\rangle =\langle A^\prime\rangle$ かつ $|A^\prime|\leq n(n-1)/2$ となるような $A^\prime$ を、 $O(n^2|A|)$ 時間で求めます。
$0\leq j\lt i\lt n$ について、 $h_{i,j}\in S_n$ を、
- $h_{i,j}=e$ または
- ( $h_{i,j}[i]=j$ かつ $h_{i,k}=k$ ($i\lt k$) )
が成り立つようにとり、これで $A^\prime=\lbrace h_{i,j} \mid 0\leq j\lt i\lt n \rbrace$ となるようにします。このために、まず $h_{i,j}=e$ として、 $A$ の要素を $1$ つずつ、以下の手順で処理します。
- 取り出した要素を $a$ とします。
- $a[n-1]=n-1$ のときは $n$ が $1$ だけ小さい場合と同じ処理になります。
- $h_{n-1,a[n-1]}=e$ のときは、 $h_{n-1,a[n-1]}\leftarrow a$ と更新して終わりです。
- $h_{n-1,a[n-1]}\neq e$ のときは、 $a\leftarrow {h_{n-1,a[n-1]}}^{-1}a$ と更新すれば $a[n-1]=n-1$ となります。
全体の答えを出す
$A$ から $G_{n-1}$ を求め、さらに $A$ から $n-1$ 番目の要素が変化しない成分 $B$ を取り出すことで、 $n$ を減らしながら再帰的に処理できます。正確には、
- $a\in \langle A\rangle \iff a\in \lbrace gb \mid g\in G_{n-1}, b\in B\rbrace$
- $b\in B$ について、 $b[n-1]=n-1$
を満たすような $B$ を求めるということです。
def solve(A, n)
A = reduce(A) # 絞り込み
G = [[] for _ in range(n)]
for m in reversed(range(1,n)) :
A, G[m] = solve_sub(A, n, m)
return G
G[n-1] を求める
$G_{n-1}$ を求めます。つまり、 $k=0,1,\ldots ,n-1$ について、 $r_o[n-1]=o$ を満たすような $r_o\in \langle A\rangle$ が存在するかどうか、また存在するなら $1$ つ求める必要があります。
$r_o\in \langle A\rangle$ が存在する $o$ 全体の集合を $O_{\text{rbit}}$ とおきます。
常に $r_{n-1}=e$ ととれて、 $n-1\in O_{\text{rbit}}$ です。
$o\in O_{\text{rbit}}$ のとき、 $a\in A$ について $r_{a[o]}=ar_o$ ととれるため、 $a[o]\in O_{\text{rbit}}$ です。これを用いてグラフ探索の要領で $O_{\text{rbit}}$ および $G_{n-1}$ が求まります。
計算量は $O(n^2+n |A|)$ です。
B を求める
$\langle B\rangle =\lbrace p \in \langle A\rangle \mid p[n-1] \rbrace$ を満たす $B$ を求めます。
結論としては、 $B=\lbrace {r_{a[o]}}^{-1} a r_o \mid o\in O_{\text{rbit}} , a\in A \rbrace$ とすればよいです。
$p\in B$ について、 $p$ を $A$ の要素の積で表したとします。
$$p=a_0a_1a_2 \cdots a_{q-1}$$
$n-1$ が $t$ 番目の要素に移動するタイミングで $r_tr_t^{-1}$ を挟むことで、 $n-1$ を一旦もとの位置に戻します。
$$p= a_0 (r_{t_1} {r_{t_1}}^{-1}) a_1 (r_{t_2} {r_{t_2}}^{-1}) a_2 (r_{t_3} \cdots {r_{t_{q-1}}}^{-1}) a_{q-1}$$
つまり
$$p= (e a_0 r_{t_1}) ({r_{t_1}}^{-1} a_1 r_{t_2}) ({r_{t_2}}^{-1} a_2 r_{t_3}) \cdots ({r_{t_{q-1}}}^{-1} a_{q-1} e)$$
$$p= ({r_{n-1}}^{-1} a_0 r_{t_1}) ({r_{t_1}}^{-1} a_1 r_{t_2}) ({r_{t_2}}^{-1} a_2 r_{t_3}) \cdots ({r_{t_{q-1}}}^{-1} a_{q-1} r_{n-1})$$
よって、
$$B=\lbrace {r_\alpha }^{-1} a r_\beta \mid 0\leq \alpha\lt n , 0\leq \beta \lt n, a\in A , {r_\alpha }^{-1} a r_\beta[n-1]=n-1\rbrace$$
が条件を満たします。言い換えにより、
$$B=\lbrace {r_{a[o]}}^{-1} a r_o \mid o\in O_{\text{rbit}} , a\in A \rbrace$$
となります。 $B$ の要素数はおよそ $n|A|$ になりますが、これを絞り込みによって $O(n^2)$ 個に変形することで計算量は抑えられます。
まとめて完成
絞り込みにより、 $O(n^2|A|)$ 時間で $|A|$ が $O(n^2)$ である問題に帰着します。
$G_{n-1}$ を求めるのに $O(n^3)$ 時間かかります。 $|B|$ は $O(n^3)$ になり、その計算に $O(n^4)$ 時間かかります。これを再度絞り込みにかけると $O(n^5)$ 時間かかります。
よって、 $G_{n-1},G_{n-2},\ldots $ と順に求めてゆくと、 $O(n^6)$ 時間で目的のものが得られます。