問題
左側 $N_L$ 頂点・右側 $N_R$ 頂点の二部グラフがあり、辺は $M$ 本ある。辺 $i$ は左の頂点 $a_i$ と右の頂点 $b_i$ を結ぶ。
ある頂点 $v$ に駒を置き、$v$ を「訪問済み」にする。先手から交互に、駒を隣接する未訪問の頂点へ動かし、動かした先を訪問済みにする。動かせなくなった人の負け。
すべての頂点 $v$ について、$v$ から始めたときに先手と後手のどちらが勝つかを求めよ(両者最善)。
入力形式
N_L N_R M
a_1 b_1
...
a_M b_M
出力形式: $N_L+N_R$ 行。まず左の頂点 $1..N_L$、続いて右の頂点 $1..N_R$ の順に、先手が勝つなら First、後手が勝つなら Second。
制約
| # | 条件 |
|---|---|
| 1 | $1\le N_L,N_R\le 10^5$ |
| 2 | $0\le M\le 2\times10^5$ |
| 3 | $1\le a_i\le N_L$、$1\le b_i\le N_R$。多重辺はない |
入出力例
入力例1
2 2 3
1 1
2 1
2 2出力例1
First
First
First
First(左1−右1−左2−右2 の 4 頂点の路。どこから始めても先手勝ち)
入力例2
1 3 3
1 1
1 2
1 3出力例2
First
Second
Second
Second(星。中心から始めれば先手は葉へ動いて勝つ。葉から始めると先手は中心へ動くしかなく、後手が別の葉へ動いて勝つ)
入力例3
3 2 4
1 1
2 1
2 2
3 2出力例3
Second
Second
Second
First
First(左1−右1−左2−右2−左3 の 5 頂点の路。3 例とも全状態のミニマックス探索と一致)
背景と出典
無向グラフ上の頂点ジオグラフィ(Undirected Vertex Geography)について、「$v$ から始めて先手が勝つ ⇔ $v$ がグラフのすべての最大マッチングに含まれる」という定理がある。Fraenkel・Scheinerman・Ullman(1993, *Undirected edge geography*, Theoretical Computer Science)で示された、と私は記憶している(原典は本セッションでは確認していない。定理そのものは一般グラフ 2011 頂点ぶんの全探索で照合した。下の検証ログ)。
有向グラフ版のジオグラフィは PSPACE 完全として知られる、と私は記憶している(同じく未確認)。無向にすると多項式時間で解けるのは、相手の手に「マッチングの相手へ動く」で応じ続ける戦略があるから。
全最大マッチングに含まれる頂点の判定は、Day148-Q3 の Dulmage–Mendelsohn 分解と同じ交互路の到達で求まる。本問は自作。
概念図
ヒント(クリックで段階的に開示)
ヒント1(方向性)
状態は「駒の位置+訪問済み集合」で指数個。小さいグラフで勝敗を全探索し、「先手が勝つ頂点」に共通する性質を探す。次数や、ある 1 つのマッチングで使われているかでは決まらない。
ヒント2(アプローチ)
最大マッチング $M$ が $v$ を含むとき、先手は「いつも駒のいる頂点の $M$ の相手へ動く」とする。後手が動いた先がいつも $M$ で覆われていれば先手は動き続けられる。覆われていない頂点に着いたら、路を反転させると $v$ を含まない最大マッチングが作れる。だから「すべての最大マッチングに $v$ が入る」が先手勝ちの条件になる。
ヒント3(誘導)
二部グラフで最大マッチング $M$ を 1 つ求める。左の頂点 $u$ が「ある最大マッチングで使われない」⇔ 左の自由頂点から、左→右は任意の辺、右→左は $M$ の辺でたどって $u$ に着く。右側も同様に右の自由頂点から BFS。着いた頂点は Second、着かない頂点は First。
模範解答 (Python)
模範解答を表示する(まず自力で解いてから)
import sys
from collections import deque
def hopcroft_karp(nl, nr, adj):
INF = 1 << 30
ml = [-1] * nl
mr = [-1] * nr
while True:
dist = [INF] * nl
q = deque()
for u in range(nl):
if ml[u] < 0:
dist[u] = 0
q.append(u)
found = False
while q:
u = q.popleft()
for v in adj[u]:
w = mr[v]
if w < 0:
found = True
elif dist[w] == INF:
dist[w] = dist[u] + 1
q.append(w)
if not found:
return ml, mr
it = [0] * nl
for s in range(nl): # 反復 DFS で増加路を探す
if ml[s] >= 0:
continue
stack = [s]
while stack:
u = stack[-1]
if it[u] < len(adj[u]):
v = adj[u][it[u]]
it[u] += 1
w = mr[v]
if w < 0: # 増加路が見つかった: スタックに沿って反転
while stack:
uu = stack.pop()
vv = adj[uu][it[uu] - 1]
ml[uu] = vv
mr[vv] = uu
break
if dist[w] == dist[u] + 1:
stack.append(w)
else:
dist[u] = INF
stack.pop()
def main():
data = sys.stdin.buffer.read().split()
nl, nr, m = int(data[0]), int(data[1]), int(data[2])
adj = [[] for _ in range(nl)]
radj = [[] for _ in range(nr)]
p = 3
for _ in range(m):
a, b = int(data[p]) - 1, int(data[p + 1]) - 1
p += 2
adj[a].append(b)
radj[b].append(a)
ml, mr = hopcroft_karp(nl, nr, adj)
# 左の頂点 u が「ある最大マッチングで使われない」
# ⇔ 左の自由頂点から 交互路(左→右は非マッチ辺、右→左はマッチ辺)で偶数歩で着く
freeL = [False] * nl
q = deque(u for u in range(nl) if ml[u] < 0)
for u in q:
freeL[u] = True
while q:
u = q.popleft()
for v in adj[u]:
w = mr[v]
if w >= 0 and not freeL[w]:
freeL[w] = True
q.append(w)
freeR = [False] * nr
q = deque(v for v in range(nr) if mr[v] < 0)
for v in q:
freeR[v] = True
while q:
v = q.popleft()
for u in radj[v]:
w = ml[u]
if w >= 0 and not freeR[w]:
freeR[w] = True
q.append(w)
out = ['Second' if f else 'First' for f in freeL]
out += ['Second' if f else 'First' for f in freeR]
sys.stdout.write('\n'.join(out) + '\n')
if __name__ == "__main__":
main()
計算量: Hopcroft–Karp が $O(M\sqrt{N})$、BFS 2 回が $O(N+M)$。$N_L=N_R=10^5$、$M=2\times10^5$ のランダムなグラフで 2.0 s(Python 3.14.7、M1 MacBook Pro、負荷平均 23〜27 の混んだ時間帯での実測、起動と入出力を含む)。
Step-by-Step 解説
状態は(駒の位置、訪問済み集合)。小さいグラフなら lru_cache で勝敗が出る。そこで「次数が正なら先手勝ち」と比べると 4153 頂点中 1382 頂点で外れ、「求めた 1 つの最大マッチングで使われていれば先手勝ち」でも 729 頂点で外れる(実測)。どちらでもない。
最大マッチング $M$ を 1 つ固定する($v$ は必ず覆われている)。先手はいつも「今の頂点の $M$ の相手」へ動く。後手がどこかへ動いたとき、そこが $M$ で覆われていて相手が未訪問なら先手はまた動ける。
もし後手が $M$ で覆われていない頂点 $x$ に動けたとすると、ここまでの道 $v\to v_1\to\dots\to x$ は「$M$ の辺・非 $M$ の辺」が交互に並び、$x$ が自由頂点なので、この道に沿って $M$ を入れ替えると大きさが同じで $v$ を含まない最大マッチングができる。これは仮定に反する。相手が訪問済みのケースも、道が交互になっていることから起こらない。よって先手は必ず動ける=先手勝ち。
$M'$ では $v$ は自由。先手がどこへ動いても、その先 $y$ は $M'$ で覆われている(覆われていなければ辺 $vy$ を $M'$ に足せて最大性に反する)。後手は $y$ の $M'$ の相手へ動く。以下 Step 2 と同じ議論で、後手はいつも動ける=後手勝ち。
二部グラフで最大マッチング $M$ を 1 つ求める。左の頂点 $u$ を含まない最大マッチングがあるのは、$u$ が自由か、左の自由頂点 $f$ から「左→右(非 $M$ 辺)→左($M$ 辺)」をくり返して $u$ に着けるとき(その偶数長の交互路を反転すれば $u$ が自由になる)。右側も右の自由頂点から同じように BFS。
Hopcroft–Karp の DFS は再帰にすると深さが $10^5$ を超えうるので、スタックで書いた。増加路が見つかったら、スタックに積まれた左頂点ごとに「直前にたどった辺」adj[u][it[u]-1] でマッチを張り替える。
迷った他案と採らなかった理由
| 案 | 計算量 | 採否 | 理由 |
|---|---|---|---|
| 全状態のミニマックス | $O(2^N N)$ | 検算用のみ | 12 頂点まで。4153 頂点の照合に使用 |
| 頂点ごとに $v$ を消して最大マッチングを取り直す($\nu(G-v)<\nu(G)$ か) | $O(N\cdot M\sqrt N)$ | 不採用 | 一般グラフでも使える判定だが、$N=2\times10^5$ では $10^5$ 回のマッチングになる。一般グラフ版の定理の照合(ビット DP のマッチング)にだけ使った |
| 一般グラフで Edmonds の花法+Gallai–Edmonds 分解 | $O(N^3)$ など | 不採用(範囲外) | Gallai–Edmonds 分解の $D$(ある最大マッチングで自由になれる頂点の集合)を求めれば $v\notin D$ で判定できるが、花法の実装が要る。本セッションでは実装しておらず、問題を二部グラフに限った |
| 最大マッチング 1 回+交互路 BFS 2 回(本解) | $O(M\sqrt N)$ | 採用 | 2.0 s |
前提が変わったら:
- 一般グラフなら、定理はそのまま成り立つ(ビット DP で 2011 頂点を照合)が、判定には花法が要る。Gallai–Edmonds 分解で「ある最大マッチングで自由になれる頂点の集合 $D$」を求め、$v\notin D$ なら
First - 辺を訪問済みにする版(Undirected Edge Geography)は別の問題で、本解は使えない
- 駒が 2 つ、または和のゲームにすると、勝敗だけでなく Grundy 数が要る。マッチングの議論は Grundy 数を教えてくれない(本セッションでは未検討)
よくあるミス
| ミス | 原因 | 正しい書き方 |
|---|---|---|
| 「求めた最大マッチングで使われていれば先手勝ち」 | 最大マッチングは 1 つではない | 4153 頂点中 729 頂点で誤答(実測)。「すべての最大マッチングに入るか」を交互路で判定する |
| 次数が正なら先手勝ち | 1 手目は打てても、その後の応酬で負ける | 4153 頂点中 1382 頂点で誤答(実測) |
| 左の自由頂点からの BFS で右の頂点にも印を付ける | 右の頂点が「自由になれるか」は右の自由頂点からの交互路で決まる | 左右それぞれ、自分の側の自由頂点から BFS する |
| BFS の右→左で任意の辺をたどる | 交互路でなくなる | 右→左はマッチ辺 mr[v] だけ |
| Hopcroft–Karp の DFS を再帰で書く | 深さ $10^5$ 超で落ちる | スタックで書く |
次のステップ
- 発展1: 一般グラフ版(Edmonds の花法+Gallai–Edmonds 分解)
- 発展2: 辺を訪問済みにする版(Undirected Edge Geography)
- 発展3: Fraenkel–Scheinerman–Ullman(1993)の原典を確認する(本セッションでは未確認)
検証ログ(本セッションで実行した数値)
- 全状態ミニマックスとの照合(実測): 左右 1〜6 頂点・辺の確率 0.2〜0.8 のランダム二部グラフ 600 個、計 4153 頂点で不一致 0(
First1769 頂点) - 定理そのものの照合(実測): 1〜9 頂点のランダム一般グラフ 400 個、計 2011 頂点で「ミニマックスの勝敗」と「$\nu(G-v)<\nu(G)$(ビット DP の最大マッチング)」が全一致
- 誤った方法(実測): 「次数が正なら先手勝ち」は 1382/4153 頂点、「求めた 1 つの最大マッチングで使われていれば先手勝ち」は 729/4153 頂点で誤答
- 入力例(実測): 3 例とも上の出力と一致
- 実行時間(実測、起動と入出力込み、負荷平均 23〜27): $N_L=N_R=10^5,M=2\times10^5$ で 2.0 s、$M=4\times10^5$ で 6.99 s、$N_L=N_R=2\times10^5,M=4\times10^5$ で 5.27 s。後者 2 つのため制約を $M\le2\times10^5$ にした
- 扱っていない範囲: 一般グラフ(花法)、辺ジオグラフィ、和のゲーム、原典の確認