各連結成分が何らかの値を持っているときのUnion Findの書き方
概要
最近F - Construct HighwayやA - Reachable Townsが立て続けに解けなくて悲しかったので、お互いに共通する「UnionFindの各連結成分が変数を持っていて、mergeするときにその変数を使ってなんかする」みたいな問題をすっとかけるようにしたいなと思い、考え方を記すことにした。 他にも使う問題があったら足していきたい/教えてほしい。
考え方
各連結成分が持つ変数のことをで表すことにする。両方の問題で言えることだが、
- はどのような値なのか?
- どのを持つ連結成分とマージするのか?
- どうやってマージするのか?
を考える必要が出てくる。以下それぞれの問題に対して順番に考えていく。
1. はどのような値なのか?
ここがうまいこと設定できればこの解き方で解ける可能性が高そう。
F - Construct Highwayについてはその連結成分のうち、次数が足りない頂点群をとすればよい。(例えば、ならその連結成分についてはまだ頂点1は2回、頂点3は1回、頂点4は1回別の連結成分とつなぐ必要があることを表す)
A - Reachable Townsについては、各連結成分が持つ点のy座標の最小値をとすればよい。
2. どのを持つ連結成分とマージするのか?
すべての連結成分をなめてその都度を見ているとかかり、TLEになってしまう。そこで、各がうまく管理されるようにしといてやる必要がある。を管理するために、各連結成分について代表点をと結びつけておく。
二つの問題に共通していることだが、必要なのはというペアが適切にソートされていて、削除や追加、検索にそんなに時間を要さないこと。ということは、setを使えばうまくいきそうだということがわかる。従って、F - Construct Highwayについては、というペアをの長さの降順にソートされるようにsetに入れておけばよさそうだし、A - Reachable Townsについてはというペアをそのままsetに入れておけばよさそう(デフォでsetはless<int>()でsortするので)。
そうしたら、あとはsetの先頭と比較していけばよい。F - Construct Highwayなら先頭の二つをmergeしていけばいい。A - Reachable Townsならが見ている点のy座標より小さいものについてずっとmergeを繰り返していく。
3. どうやってマージするのか?
ここまでくればもう後は自然に書けそう。ここの処理は当然問題によるが、一応書いておく。
F - Construct Highwayについて、とがmergeされるときは、setから二つをpopしてきて、との末尾頂点をpop_backする。そのあと二つを融合させたがsizeが1以上ならをsetに再挿入。(なんかのサイズがでかいときにTLEしそうな感じもある?その場合setからコピー&popするんじゃなくてうまいこと参照とか使う必要があるかも、後でmergeしてから二つ目の要素をeraseする方法も試して、時間を比べてみる)
A - Reachable Townsについて、と現在見ている頂点(ただしはのy座標)がmergeされるときは、setからをpopしてきてとをmergeする。これをとなるについて繰り返したのち、それらの中で最も小さいy座標をとしたときにをsetに再挿入すればよい。