問題
$N$ 頂点 $M$ 辺の無向グラフ $G$ の彩色多項式 $P(G, k)$ を求めよ。
$$P(G, k) = \sum_{i=0}^{N} c_i \cdot k^i$$
各係数 $c_i$($i = 0, 1, \ldots, N$ の順)を出力せよ。
制約
| パラメータ | 範囲 |
|---|---|
| $N$ | $1 \le N \le 15$ |
| $M$ | $0 \le M \le N(N-1)/2$ |
| グラフ | 無向・単純グラフ |
| 時間制限 | 2秒 |
入出力例
入力例 1 (4頂点サイクル C₄)
4 4
1 2
2 3
3 4
4 1
出力例 1
0 -3 6 -4 1
$C_4$ の彩色多項式: $P = k^4 - 4k^3 + 6k^2 - 3k = k(k-1)(k^2-3k+3)$。係数: $c_0=0, c_1=-3, c_2=6, c_3=-4, c_4=1$。
入力例 2 (辺なし K̄₃)
3 0
出力例 2
0 0 0 1
辺なし3頂点グラフ: $P = k^3$。係数: $c_0=c_1=c_2=0, c_3=1$。
概念図: 包除原理による彩色多項式計算
ヒント(段階的開示)
ヒント1: 方向性
Deletion-Contraction: $P(G, k) = P(G - e, k) - P(G / e, k)$。
辺 $e$ を「削除」した場合と「収縮(両端をマージ)」した場合の差。
$N \le 15$ なら包除原理アプローチが実装しやすい。
ヒント2: アプローチ(包除原理)
$$P(G, k) = \sum_{S \subseteq E} (-1)^{|S|} k^{c(S)}$$
ここで $c(S)$ = 辺集合 $S$ が張る連結成分数(全 $N$ 頂点を含む)。
- 全部分集合 $S$ を列挙($2^M$ 通り)
- 各 $S$ に対して Union-Find で $c(S)$ を計算
- $\text{coeffs}[c(S)]$ に $(-1)^{|S|}$ を加算
ヒント3: コード骨格
coeffs = [0] * (N + 1)
def find(par, x):
while par[x] != x: par[x] = par[par[x]]; x = par[x]
return x
for mask in range(1 << M):
par = list(range(N + 1))
cnt = bin(mask).count('1')
for i in range(M):
if (mask >> i) & 1:
u, v = edges[i]
pu, pv = find(par, u), find(par, v)
if pu != pv: par[pu] = pv
c = sum(1 for v in range(1, N+1) if find(par, v) == v)
sign = 1 if cnt % 2 == 0 else -1
coeffs[c] += sign
print(' '.join(map(str, coeffs)))
模範解答 (Python)
import sys
input = sys.stdin.readline
def solve():
N, M = map(int, input().split())
edges = []
for _ in range(M):
u, v = map(int, input().split())
edges.append((u, v))
# 包除原理: P(G,k) = Σ_{S⊆E} (-1)^|S| * k^{c(S)}
# c(S) = S の辺で結ばれた N 頂点の連結成分数
coeffs = [0] * (N + 1)
def find(par, x):
while par[x] != x:
par[x] = par[par[x]]
x = par[x]
return x
for mask in range(1 << M):
par = list(range(N + 1))
cnt = 0
for i in range(M):
if (mask >> i) & 1:
u, v = edges[i]
pu, pv = find(par, u), find(par, v)
if pu != pv:
par[pu] = pv
cnt += 1
# 連結成分数(全 N 頂点を考慮)
c = sum(1 for v in range(1, N + 1) if find(par, v) == v)
sign = 1 if cnt % 2 == 0 else -1
coeffs[c] += sign
print(' '.join(map(str, coeffs)))
solve()
Step-by-Step 解説
1彩色多項式の定義
$P(G, k)$ = $k$ 色を使って隣接頂点が異なる色になるように塗る方法数。これは $k$ の多項式。 $P(G, 0) = 0$、$P(K_n, k) = k(k-1)(k-2)\cdots(k-n+1)$。
$P(G, k)$ = $k$ 色を使って隣接頂点が異なる色になるように塗る方法数。これは $k$ の多項式。 $P(G, 0) = 0$、$P(K_n, k) = k(k-1)(k-2)\cdots(k-n+1)$。
2包除原理の適用
「辺集合 $S$ の全辺の両端が同色」の方法数 = $k^{c(S)}$($c(S)$ は Union-Find で求める連結成分数)。 符号付き和:$P(G,k) = \sum_{S \subseteq E} (-1)^{|S|} k^{c(S)}$。
「辺集合 $S$ の全辺の両端が同色」の方法数 = $k^{c(S)}$($c(S)$ は Union-Find で求める連結成分数)。 符号付き和:$P(G,k) = \sum_{S \subseteq E} (-1)^{|S|} k^{c(S)}$。
3多項式の係数抽出
各 mask について $c(S)$ を求め、$(-1)^{|S|}$ を $\text{coeffs}[c(S)]$ に加算。 最終的に $\text{coeffs}[i]$ が $k^i$ の係数。
各 mask について $c(S)$ を求め、$(-1)^{|S|}$ を $\text{coeffs}[c(S)]$ に加算。 最終的に $\text{coeffs}[i]$ が $k^i$ の係数。
4Deletion-Contraction の関係
$P(G-e, k) = P(G, k) + P(G/e, k)$(包除原理の特殊ケース)。 収縮 $G/e$ は両端をマージした $N-1$ 頂点グラフ。
$P(G-e, k) = P(G, k) + P(G/e, k)$(包除原理の特殊ケース)。 収縮 $G/e$ は両端をマージした $N-1$ 頂点グラフ。
計算量
全部分集合の列挙: $O(2^M)$
各 mask での Union-Find: $O(M \alpha(N))$
連結成分数の計算: $O(N)$
全体: $O(2^M \cdot (M + N))$
注意: $N=15$ では $M \le 105$, $2^{105}$ は計算不可。実用は $M \le 25$ 程度まで
各 mask での Union-Find: $O(M \alpha(N))$
連結成分数の計算: $O(N)$
全体: $O(2^M \cdot (M + N))$
注意: $N=15$ では $M \le 105$, $2^{105}$ は計算不可。実用は $M \le 25$ 程度まで
よくあるミス
| ミス | 原因 | 正しい書き方 |
|---|---|---|
| 連結成分数の誤り | 辺の端点のみを数える | range(1, N+1) 全頂点を確認 |
| cnt の計算 | Union した回数ではなく辺選択数を数える | cnt は popcount(mask) |
| 係数の出力順 | $k^N$ から $k^0$ の順にしてしまう | coeffs[0] から coeffs[N] の順 |
| par の初期化忘れ | 各 mask で par を使い回す | par = list(range(N+1)) を mask ごとに |
次のステップ
- 発展問題: Deletion-Contraction の再帰実装 + メモ化($M > 25$ 対応)
- 関連: Tutte 多項式 $T(G; x, y)$(彩色多項式の一般化。$T(G; 1+k, 1) = P(G, k)/k^{c(G)}$)
- 応用: 信頼性多項式 $R(G, p)$(各辺が確率 $p$ で生き残るときの連結確率)