問題
N頂点M辺の無向グラフ G が与えられる。G の全域木の数を $10^9+7$ で割った余りを求めよ。各辺に重み $w_e$ が付いている場合、重み付き全域木の総数も求めよ。
制約
$2 \le N \le 400$
$0 \le M \le N(N-1)/2$
重み $w \le 10^9$
入出力例
入力例 1
4 5
1 2 1
1 3 2
1 4 3
2 3 4
3 4 5
出力例 1
8
ヒント (段階的開示)
ヒント1: 方向性
行列木定理:グラフのラプラシアン行列 L の任意の (N-1)×(N-1) 余因子の行列式が全域木の数。
ヒント2: アプローチ
L = D - A を構築。1行1列削除した行列の行列式を Gaussian elimination + modular inverse で計算。重み付きの場合、A[i][j] = w_{ij}, D[i][i] = Σ w_{ij}。
ヒント3: 誘導
def det(mat, mod):
n = len(mat)
result = 1
for i in range(n):
# ピボット選択 → スワップ → 消去
...
return result
模範解答 (Python)
import sys
input = sys.stdin.readline
MOD = 10**9 + 7
def det_mod(mat, mod):
n = len(mat)
mat = [row[:] for row in mat]
result = 1
for i in range(n):
pivot = -1
for j in range(i, n):
if mat[j][i] % mod != 0:
pivot = j; break
if pivot == -1:
return 0
if pivot != i:
mat[i], mat[pivot] = mat[pivot], mat[i]
result = (-result) % mod
result = result * mat[i][i] % mod
inv = pow(mat[i][i], mod-2, mod)
for j in range(i+1, n):
if mat[j][i] == 0: continue
factor = mat[j][i] * inv % mod
for k in range(i, n):
mat[j][k] = (mat[j][k] - factor * mat[i][k]) % mod
return result % mod
def main():
N, M = map(int, input().split())
edges = []
for _ in range(M):
u, v, w = map(int, input().split())
u -= 1; v -= 1
edges.append((u, v, w))
L = [[0]*N for _ in range(N)]
for u, v, w in edges:
L[u][u] = (L[u][u] + 1) % MOD
L[v][v] = (L[v][v] + 1) % MOD
L[u][v] = (L[u][v] - 1) % MOD
L[v][u] = (L[v][u] - 1) % MOD
cofactor = [row[:N-1] for row in L[:N-1]]
spanning_trees = det_mod(cofactor, MOD)
print(spanning_trees)
Lw = [[0]*N for _ in range(N)]
for u, v, w in edges:
Lw[u][u] = (Lw[u][u] + w) % MOD
Lw[v][v] = (Lw[v][v] + w) % MOD
Lw[u][v] = (Lw[u][v] - w) % MOD
Lw[v][u] = (Lw[v][u] - w) % MOD
cofactor_w = [row[:N-1] for row in Lw[:N-1]]
weighted_sum = det_mod(cofactor_w, MOD)
print(weighted_sum)
main()
Step-by-Step 解説
1ラプラシアン行列の構築
L = D - A。D[i][i] = 頂点 i の次数、A[i][j] = i-j 間に辺があれば1。重み付きは辺の重みを使う。
L = D - A。D[i][i] = 頂点 i の次数、A[i][j] = i-j 間に辺があれば1。重み付きは辺の重みを使う。
2余因子行列
任意の1行1列(通常は0行0列)を削除した (N-1)×(N-1) 行列を使う。どの行列を削除しても行列式は同じ。
任意の1行1列(通常は0行0列)を削除した (N-1)×(N-1) 行列を使う。どの行列を削除しても行列式は同じ。
3行列式の計算(Gaussian elimination mod p)
ガウス消去法で上三角化し、対角成分の積。mod p での各ステップで逆元を使う。
ガウス消去法で上三角化し、対角成分の積。mod p での各ステップで逆元を使う。
4重み付き全域木の解釈
重み付きラプラシアンの行列式は各全域木の辺の重みの積の総和に等しい。
重み付きラプラシアンの行列式は各全域木の辺の重みの積の総和に等しい。
よくあるミス
| ミス | 原因 | 正しい書き方 |
|---|---|---|
| 行/列の削除間違い | 余因子は同じ行列を削除 | 同じインデックスの行と列を削除 |
| ピボットが0 | 部分ピボット探索が必要 | 0でない行をスワップ |
| 符号のミス | スワップで符号が変わる | スワップごとに result *= -1 |
次のステップ
- 発展: 有向グラフの全域樹形の数(有向版)
- 応用: 辺の制約付き全域木の数