CODE FESTIVAL 2017 Elimination Tournament (名前が長い!) の F 問題 の想定解よりも善い解法です. atcoder.jp
問題概要
以下の条件を満たすような
頂点の無向グラフは何通りあるか.(頂点は区別する)
- グラフは単純で連結
- 頂点
の次数は
の総和が
のとき(つまり,グラフが木のとき),答えは
である.
では, の総和が
のとき(つまり,グラフが俗に言う「なもり木」*1のとき) はどうか?
想定解は ですが,実は
で解くことができます.
Prüfer Code
Prüfer Code は,各要素が のいずれかである長さ
の数列です.
通りある Prüfer Code と
頂点のラベル付き無向木の間には,1対1対応が存在します.
木に対する Prüfer Code は,以下の操作を 回繰り返して得られる長さ
の数列
です.
回目の操作とする.現在の葉のうち,最も番号の若いものを選ぶ(これを
とおく).
と唯一隣接している頂点の番号を
とし,
を木から取り除く.
次のように考えると,Prüfer Code から木を復元できることがわかります.
回目の操作における「現在の葉」がわかれば,そのうち最も番号の若いものが
と判明します.
- ここで,以前の操作で取り除いた頂点たち,つまり
は明らかに葉になりえません.また,
は現在の内点*2であり,逆に現在の内点はここに現れます(図を描いて確かめてみよう).したがって,
以外の頂点が現在の葉です.
回目の操作で取り除かれた辺は
頂点
を結ぶものです.全操作を終えた後は
頂点の木になるはずなので,
として登場しなかった
頂点間に最後まで残った
辺があります.
たとえば,以下のグラフに対応する Prüfer Code は です.

上の2つは互いに逆の操作になっています.あらゆる に対して「復元」によって木が作れるので,1対1対応があるとわかりました.
解法
Prüfer Code の考え方を応用します.
まずは木のときを確かめます.木の Prüfer Code において, の登場回数は
に等しいことから,木のときの問題は以下のように言い換えられます.
長さ
の数列であって,
が
回現れるものは何通りか.
これは簡単に解けて,結果は問題概要にある通りになります.
これを参考に,なもり木における「Prüfer Code もどき」を以下のように定義します.
を
個含む,長さ
の数列.
- 「Prüfer Code もどき」からの なもり木 の復元は,以下のようにして行う.
- 残りの要素がuniqueでない間は,木のときと同様に「現在の葉」を求め,最も番号の若い葉と
を辺でつなぐ操作を繰り返す.
- 残りの要素がuniqueになったら,残りの要素を円状に並べ,隣接する
つの番号の頂点を辺でつなぐ.
- 残りの要素がuniqueでない間は,木のときと同様に「現在の葉」を求め,最も番号の若い葉と
たとえば, という「Prüfer Code もどき」からは,以下の なもり木 が復元されます.

わかりやすさのために,
- 閉路の長さが
の なもり木のことを 「
-なもり木」,
- 「Prüfer Code もどき」のうち
- 末尾
要素がuniqueなものを「
-もどき」,
- uniqueな接尾辞の最大長が
,つまり "
-もどき" だが "
-もどき" ではないものを「ちょうど
-もどき」
- 末尾
と呼ぶことにします.「ちょうど -もどき」は必ず「
-なもり木」を復元することに気を付けてほしい.なもり木の閉路は必ず長さ
以上なので,
として考えます.
数え上げに落とす
ある
-なもり木 を復元する ちょうど
-もどき は何通りか.
実は,末尾 要素以外は一意に定まります.これは直感的に明らかだし,実際に帰納法から導けます.末尾
要素は閉路を導く部分ですが,ここは任意の
-なもり木 について
通りです(左回り・右回りで
通り,特定の要素を何番目に置くかで
通り).
したがって, -なもり木 の個数は,ちょうど
-もどき の個数を
で割った値に等しくなります.ちょうど
-もどき の個数は
-もどきの個数から
-もどきの個数を引けば求まるので,全ての
について
-もどき の個数が求まれば解けます.
という挿入DPを定義すると,
という漸化式で解くことができます(なお,).
以上より,この問題を で解くことが出来ました.いゃっほー!
参考文献
*1:「なもり」という漫画家がおり,Twitterでのアイコンがグラフに似ていることから.
*2:葉でない頂点のこと.