競プロ典型90問 035

Preserve Connectivity(★7)

samekard
algorithm jp

--

問題概要

木が与えられる。

その中からいくつかの頂点が選ばれる。選ばれた頂点をすべて結ぶ木(元の木を辺を使う)を見つける。このとき辺の数が最小になるように見つける。

読者の前提

想定解法に目を通した者

深さ優先探索で番号付け -> 計算

(A) 片方がもう片方のパスの上にある

深さ優先で行きがけに番号を振ると、根に近い方が先になる。

1までの計算が終わっているとする。2の高さを加えてから、1と2の共通の先祖(の一番近いもの)(つまり1)の高さを引くと、1から2までの辺が加えられることになる。

(B) 2つはどこかで分かれる

1までの計算が終わっているとする。2の高さを加えてから、1と2の共通の先祖の高さを引くと共通の先祖から2までの辺が加えられることになる。

(C) 1番端同士

ソースをよく観察すると端同士で計算を行なっているところがある。その理由は

  • 1番目の頂点の高さを加える
  • 2番目の頂点の高さを加える
  • 1番目と2番目の共通の先祖の高さを引く
  • 以下繰り返し
  • 最後と最後から2番目の共通の先祖の高さを引く

とすると値は左の図の辺の長さの合計になる。これは「もともとの木の根」から「選ばれた辺で作られる木の根」までの高さが余計に加えられている。

そこで右のように端同士の共通の先祖の高さ求める。この共通の先祖は選ばれた辺で作られる木の根であり、この高さ引くと余計な分がなくなる。

2つの頂点の共通の先祖の計算

Yの字をイメージすると、右上の頂点と左上の頂点の共通の先祖は3本の線が触れ合っている点である。ここを見つける。

ざっくり言うと二分探索をしているが、すこしトリッキーなので図解する。

現在の候補の範囲の真ん中を調べて2点の先祖が同じなら、下半分を残し、またその半分のところを調べる。

現在の候補の範囲の真ん中を調べて2点の先祖が違っているなら、その違っている頂点に移動して上半分を残し、またその半分のところを調べる。

どっちのパターンでも、32個先を調べたら次は16個先だし、4個先を調べたら次は2個先になるので、調べる距離は毎回半分になっていく。

--

--

samekard
algorithm jp

iOSアプリをいろいろ作りました。英語と中国語を勉強中。