目次

UNIQUE VISION Programming Contest 2024 Summer (AtCoder Beginner Contest 359) G問題メモ

UNIQUE VISION Programming Contest 2024 Summer (AtCoder Beginner Contest 359)

G - Sum of Tree Distance

G - Sum of Tree Distance

いろんな解法がある。

問題

解法1 - 平方根で場合分け

同じ色の頂点が何個あるかで色分けする。
閾値 $B$ を決める。

$B$ 個未満の色

同じ色が $B$ 個未満の場合、愚直に全ペアの距離を1つ1つ求める。
オイラーツアー+区間最小値が取得できるSparseTable を前計算しておくことにより、2頂点間の距離は $O(1)$ で求められる。
(言語によってはbit_lengthを求める際に軽い $O(\log{N})$ がかかるが)

1つの色につき最大 $O(B^2)$、全体では $O(B^2 \dfrac{N}{B})$ かかる。
区間最小値をセグ木でやるとlogがつき、若干TLEが厳しくなる。

$B$ 個以上の色

同じ色が $B$ 個以上の頂点は、木DPする。

1つの色につき $O(N)$、全体では $O(N \dfrac{N}{B})$ かかる。


この2つのバランスが取れるのは、$B = \sqrt{N}$ の時で、全体 $O(N \sqrt{N})$ となる。

PyPyだと数個のテストケースでTLEが取れなかった。
Numbaに書き直すと余裕を持って通った。(約2000ms)

Python3

解法2 - 木DP+マージテク

距離計算の部分にちょっとした工夫が必要になるが、こちらの方が実装量は少ない。
ただ、ライングラフ+色の種類が多いテストケースに対して $O(N^2)$ の空間計算量が必要になるのでMLEの危険性がある? (まぁ、定数倍が大きいわけではないし、MLEの制限はTLEと比べると緩いので大丈夫か)

1回の木DPで、全ての色について求める。

例えば解法1のDPでは「頂点 $v$ までの距離の総和」を値として持ったため、頂点を遡る毎に更新が必要になった。
しかし、マージテクをする場合は、小さい方を大きい方にマージする分、大きい方は更新が発生してはいけない。

「深さ」を値として持つことで、距離を求めることを可能にしつつ、更新が発生しないようにできる。
「子孫 $u$ から祖先 $v$ までの距離」=「$u$ の深さ - $v$ の深さ」

これで、$a$ の種類数が少ない方を多い方にマージしていくことで、時間計算量は $O(N \log{N})$ となる。
DPの値は、配列で持つと明確に $O(N^2)$ の空間を要するため、基本的には辞書で持つことになる。これに log がかかる言語では、$O(N \log^2{N})$ となる。

Python

その他の解法