ABC220F Distance Sums 2
与えられた木を、頂点1を根とする根付き木とみなします。
辺で結ばれた頂点を考えます。頂点の方が根に近いものとします。
根からDFSをすると、での探索が終了した後にに戻ります。
を根とする部分木について、の子孫についてのが求まっているとします。
から見た時、までの距離は、までの距離にが追加されたと考えることができます。式で表すと、です。
が何回足されるかを考えると、の子孫の個数+1(の分)となります。これはを根とする部分木の頂点数に一致します。
したがって、根である1からの各頂点へ距離の総和は、以下の木DPにより求まります。
:=頂点1を根とする根付き木において、頂点を根とする部分木の頂点数
頂点1を根とする根付き木において、頂点を根とする部分木の、とその子孫との距離の総和
遷移を考えます。
・
の子について、が求まっているとすると、
を根とする部分木の頂点数は、子を根とする部分木の頂点数+自身となるので、以下の通りです。
・
の子について、が求まっているとすると、からすべての子孫への距離は、子であるからの距離+となり、は、を根とする部分木の頂点数分足されるので以下の通りです。
このDPをDFSしながら解くと、根である頂点1からの各頂点の距離の総和は求まります。
しかし、今回「すべてを根とした時」について総和を求める必要があります。
この時に使えるのが全方位木DPです。
全方位木DPの詳細な解説は検索するとわかりやすい記事が出てくるので割愛します。
簡単に述べると、
(1) 頂点1を根とする木DPをする(DFSする)。
(2) 根でない頂点については、1回目のDFSで親である頂点を根とするでない側の部分木についてのみ、値が計算されていない
(3) そこで、のうち、が接続しているところを除いた部分木のDPの値を計算する
(4) 計算したDPの値をに渡す
(3),(4)のアルゴリズムとしては、の子について、を計算して、にを渡します。この処理を頂点1から順に行なっていきます。(これはBFSになるらしいです。)
といったことをします。
全方位木DPでは、頂点から見たときの頂点を根とした時の部分木、というふうに見るため、以下のようなDPとなります。
頂点を親としたときの、頂点を根とする部分木の頂点数
頂点を親としたときの、頂点を根とする部分木の、とその子孫との距離の総和
があるかないかで、遷移はほぼ同じです。
全方位木DPでは、辺に対し、始点側をとすると、
・のDP情報はが保存している
という点で、通常の木DPと異なると思います。
また、
(1) が接続する頂点のDPの値を統合しつつ(を除く)、何らかの計算を行い、
(2) その値から、を含めたDPの値を計算し直し、
その計算値を側に渡すことが必要になります。
(1)の処理だけでは、自分を含めた計算値になっていない場合があることにも留意します。
上記における、(1)の子の情報を統合して計算するのをop、(2)の自分を含めたDPの値を計算し直すのをadd_rootとして定義します。
今回の問題で実装を考えます。
と接続するについて考えます。
opでは共に、単純に子の情報を足し合わせていきます。
すると、最終的に
が計算された状態になっています。
この状態から、に関する情報を計算し直すadd_rootでは、
にを加え、に1を加えるとについての正しいDPの値となります。
もう1点気をつけたいのが、全方位木DPでは、自分に接続する番目の頂点の情報を除くために、
を予め計算値しておき、
によって、目的の値を得ます。
opはDFSでの子の情報の統合と、上記計算の3箇所で使用され、いずれも正しく値を計算できなければならないです。(コンテスト中、ここの実装でつまずいていました。)
自分の間違いとして、について、
としていましたが、これだと、を計算した時、不具合がおきます。
提出コード:https://atcoder.jp/contests/abc220/submissions/26256944
追記
ABC222Fの解説が全方位木DPについてよく書かれています。
https://atcoder.jp/contests/abc222/editorial/2749