行列式の計算方法は 掃き出し法で連立一次方程式を解く 逆行列と行列式を求めるで解説しています。ここではある行の定数倍を別の行に加えても行列式は同じであるという性質と余因子展開を用いて行数を減らすことで行列式を求めています。

すべての要素が整数であるなら行列式も整数になりますが、この処理では割り算を使うため、誤差が出る可能性があります。また要素が整数である行列の行列式を 10^9 + 7で割ったときの余りを求めるときはどうすればよいのでしょうか?

逆元

A[0] ~ A[n] の合計を m で割ったときの剰余を考える時、(A[0]+A[1]+A[2]) % m = ((A[0]+A[1]) % m+A[2]) % m なので A[0] ~ A[n] の合計を計算しなくてもループのなかで以下のような処理をすれば解を得ることができます。掛け算も同様です。これによってオーバーフローを気にする必要はなくなります。

引き算のときもほぼ同様ですが注意が必要です。
1 % 4 は 1、-3 は 1 から 4 を引いたものなので 1 % 4 == -3 % 4 となってほしいのですが、そうはなりません。-3 % 4 == -3 です。なので演算子 % を使用したときは結果が負数のときは m を足さなければなりません。

問題は割り算です。割り算のときはそうはいきません。

ところで割り算は逆数を掛けることと同じです。剰余を考えるときも逆数のようなものが存在します。

a×bを m で割ったときの余りが 1 になるとき b を a の逆元といいます。

例えば mod 13 において 2 の逆元は 7 です。(2×7を13で割ったときの余りは1なので)

逆元を求めるには?

逆元を求めるには拡張ユークリッドの互助法を用います。

行列式を計算する

使ってみる

G – Spanning Tree

問題の趣旨は以下のとおりです。

2次元配列 A が与えられる。
N 頂点のグラフ があり、A[i, j] == 1 のとき i と頂点 j を直接結ぶ辺が存在する。
A[i, j] == 0 のときは存在してはならない。
A[i, j] == -1 のときはどちらでもよい。

この条件で全域木は何通り作れるだろうか?

ラプラシアン行列と行列木定理

各行、列がそれぞれ頂点に対応した行列で、対角成分にはその頂点の次数、非対角成分については枝がある部分に ?1、ない部分に 0 を格納した正方行列をラプラシアン行列と言います。

そして行列木定理では「無向グラフ G の全域木の個数はグラフのラプラシアン行列の任意の余因子と等しい」とされています。

ラプラシアン行列の生成

そこで与えられた2次元配列からラプラシアン行列を生成します。そのまえに必ず辺が存在する部分だけで閉路が存在する場合、絶対に木にはならないのでその確認をおこないます。

そのあとそれぞれの連結成分をひとつの頂点に圧縮して、頂点と頂点の間に辺を張って全域木をつくる問題にするのですが、まず2次元配列において辺が存在してもしなくてもかまわない頂点のうち、連結成分の代表元同士で張ることにします。そのあと連結成分の内部で代表元とそれ以外の頂点で辺を張ります。こうすることで題意を満たしたラプラシアン行列を得ることができます。

連結成分を調べるにあたって ac-library-csharp を使っています。

参考:ac-library-csharpを使ってみる