TopCoder SRM 733のMedium "BuildingSpanningTreesDiv1"
解法メモ
N個の頂点を持つ完全グラフにおいて、このグラフに含まれる辺の部分集合が与えられる。グラフの部分グラフに、集合の辺を全て含む全域木がいくつ存在するか(各頂点は区別する)を計算する問題。
定式化しようと思ったけど手が出なかった。
公式の解説: Single Round Match 733 Editorials -
解法は、
- 集合の辺で閉路が形成されないかをチェックし、閉路が存在すれば0を出力。
- 集合から形成された森において、各木の頂点数を計算し、(kは森に含まれる木の数)を出力。
- 計算量は (はアッカーマン関数の逆関数)
完全グラフに含まれる全域木の個数に関してCayley's formulaが存在し、頂点がN個の場合はとなる。
この問題ではCaley's formulaを一般化した以下の式を利用する。
この問題の森において、どの木と木を繋ぐかが決まった時、それぞれの木同士を繋ぎ方は通りであることがわかるため、右辺に変形できれば解が計算できる。
一般化したCayley's formulaの参考:
combinatorics - Proof of Generalized Cayley's formula - Mathematics Stack Exchange
参考先のページでは、以下の式を示している。
(特にがなんだろうと思って)この式の意味の解釈に少し悩んだ。この式は任意のについて成り立つ。
左辺の式では、n個の頂点からなる各木のPrüfer sequenceに対し、各頂点ラベルiが出現した分だけ(つまり回)掛けた値を計算し、全ての木におけるその積の和を計算している。
ここで、Prüfer sequenceは長さn-2のラベル1~nからなる全ての組み合わせ(つまり通り)が存在するため、右辺のようにの全ての出現順の積の和になることがわかる。
解法コード (Python2)
class BuildingSpanningTreesDiv1: def getNumberOfSpanningTrees(self, n, x, y): p = range(n) def root(x): if x == p[x]: return x p[x] = y = root(p[x]) return y def unite(x, y): px = root(x); py = root(y) if px == py: return 0 if px < py: p[py] = px else: p[px] = py return 1 for s, t in zip(x, y): if not unite(s-1, t-1): return 0 g = [0]*n k = 0 for i in range(n): j = root(i) if i == j: k += 1 g[j] += 1 if k == 1: return 1 MOD = 987654323 ans = pow(n, k-2, MOD) for i in range(n): if g[i]: ans = (ans * g[i]) % MOD return ans