AtCoder NoviStepsを埋めてみる(20) SortedSet 1Qの続きです。今回は重みつきUnion-Find(別名 ポテンシャル付き Union-Find)です。

重みつきUnion-Find(ポテンシャル付き Union-Find)とその実装

普通の Union-Find は

① Find: 特定の要素がどの集合に属しているかを求める。2つの要素が同じ集合に属しているかの判定にも使われる。
② Union: 2つの集合を1つに統合する。

でしたが、重みつきUnion-Find はこれを少し発展させて、各ノード v に重み weight(v) を持たせ、ノード間の距離も管理するようなものになっています。これによって「A は B より 3 大きい」「B は C より 2 大きい」というふたつの情報から「A は C より どれだけ大きいか?」という問いにほぼ定数時間で答えることができます。また「A は B より 3 大きい」「B は C より 2 大きい」から「A は C より 10 大きい」という 3 つの条件があるとき、実はこれらは矛盾しているのですが、このような矛盾の有無を確認するときにも使えます。

重みつきUnion-Find の主な機能としては以下の 3 つがあります。

merge: weight(y) = weight(x) + w となるように x と y を merge する
same: x と y は同じグループにいるかどうかの判定
diff: x と y の重みの差を返す

D – Hidden Weights

D – Hidden Weights

問題の概要

N 頂点 M 辺の有向グラフが与えられる。
各頂点に -10^18 以上 10^18 以下の整数を書き込む方法であって、次の条件を満たすものを 1 つ出力せよ。
(条件)頂点 i に書き込まれている値を X[i] としたとき、すべての辺 j = 1, 2, …, M について X[v[j]] – X[u[j]] = w[j] が成り立つ。

同じ連結成分であれば 1 つの頂点の値が決まるとすべて決まります。このようなときに 重みつきUnion-Find が役に立ちます。

D – Relative Position

D – Relative Position

問題の概要

座標平面上に 1 から N の番号がついた N 人の人がいる。
人 1 は原点にいる。
次の形式の情報が M 個与えられる。
人 A[i] から見て、人 B[i] は、x 軸正方向に X[i]、y 軸正方向に Y[i] 離れた位置にいる。
それぞれの人がいる座標を求めよ。一意に定まらないときはその旨報告せよ。

XY 座標平面上なので X 成分と Y 成分にわけて考えます。WeightedUnionFind をふたつ生成すればよいです。

人 1 とX 成分と Y 成分の少なくともどちらかが同一連結成分に属するのであれば 人 1 の位置が原点であることからその位置が特定できます。そうではない場合は一意に定まりません。

D – People on a Line

D – People on a Line

問題の概要

x 軸上に N 人の人が立っている。人 i の位置を X[i](X[i] は 0 以上 10^9 以下の整数)とする。
同じ位置に複数の人が立っていることもありうる。
人 R[i] は人 L[i] よりも距離 D[i] だけ右にいるという情報が M 個与えられる。
与えられる M 個すべての情報に矛盾があるかどうか判定せよ。

入力を WeightedUnionFind.Merge メソッドで merge していけばよいです。途中で矛盾があれば false が返されるので矛盾を検出することができます。

F – Good Set Query

F – Good Set Query

問題の概要

Q 個の整数の 3 つ組 (A[i], B[i], D[i]) が与えられる。
集合 {1, 2, …, Q} の部分集合 S が良い集合であることを、下記の条件を満たす長さ N の整数列 X が存在することと定める。
すべての i ∈ S について X[A[i]] – X[B[i]] = D[i] が成り立つ。

S が空集合である状態から始め、i = 1, 2, …, Q の順に下記の操作をおこなう。
もし S∪{i} が良い集合なら、S を S∪{i} で置き換える。
最終的な S のすべての要素を昇順に出力せよ。

i 番目の入力がこれまでのものと矛盾していないなら i を S に追加します。矛盾しているときは無視します(merge の処理をおこなわない)。

最後に S を出力します。

F – Pay or Receive

F – Pay or Receive

問題の概要

1, …, N の番号がついた N 個の街と、1, …, M の番号がついた M 本の道路がある。
道路 i は街 A[i] と B[i] を結んでいて、A[i] から街 B[i] に移動するときにはポイントが C[i] だけ増加し、逆向きに移動すると C[i] だけ減少する。
所持しているポイントは負にもなりえる。

次の Q 個の質問に答えよ。
(質問)所持しているポイントが 0 である状態で街 X[i] から移動を始めたとき、街 Y[i] にいる状態で所持しているポイントの最大値を出力せよ。
ただし、街 X[i] から街 Y[i] に到達できないときは nan、街 Y[i] にいる状態で所持しているポイントをいくらでも増やせるときは inf を出力せよ。

閉路がない場合、WeightedUnionFind で道路で街同士を merge して Diff を取得すればそれが解となります。

閉路がある場合で、閉路を回り続けることでポイントをいくらでも増やせるときは inf が解となります。

閉路を回り続けることでポイントをいくらでも増やせるかを調べる方法ですが、まず重みの絶対値で降順ソートします。重みの絶対値が大きい順に merge していくのですが、このとき矛盾があればそこを回り続けることでポイントをいくらでも増やせる閉路があるということなので、その頂点の Leader を取得して保存しておきます。

質問に答えるときに X[i] と Y[i] が同一連結成分に属し、X[i] の Leader が保存しておいた頂点と一致するときは inf が解となります。そうでない場合は Diff(X[i], Y[i]) が解です。また tree.IsSame(X[i], Y[i]) == false のときは同一成分内に存在しないということなので、街 X[i] から街 Y[i] には到達できず nan が解となります。