ABC133-F:Colorful Tree

オイラーツアーとかはともかく、この解法が思いつけないのは悔しい...。

解法

 まず、色の変更のことは忘れて、「木上の2u,v間の距離を求めよ。」というクエリに効率的に答えることについて確認する。このクエリに答えるには、u,vの最小共通祖先(LCA:Least Common Ancestor)が重要になる。木を1を根とする根付き木だと思って、d(v)を頂点vの根からの距離とする。lca(u,v)uvLCAとすれば、u,v間の距離はd(u)+d(v)-2d(lca(u,v))と書ける。つまり、u,v,lca(u,v)についての情報さえわかれば十分だということがわかる。LCAの効率的な述べ方については、ここでは省略するが、蟻本や検索をすれば情報は手に入るはず。
 色のことを思い出してみる。ある頂点vについて「色xの辺の距離をyに変えたときに、根からの距離を求めよ。」という問いに答える必要があるが、オンラインで答えるには若干の技術が必要になる(公式解説などはこれ)。ここでは、先にすべてのクエリの情報を読み込んでしまって、その上で各クエリの答えを求めることを考えてみる。まず、各頂点に「この頂点はi番目のクエリに関わり、そのクエリは色xの辺の距離をyにしたときのことを聞いている。」という記録をしておく。そして根からdfsをして、mapなどのデータ構造などで、「今見ている頂点をvとして、vに着くまでに辿った各色の辺の距離の総和と本数」を管理しておく。vに来たら、保存していたクエリについて、mapのデータを参照しながら適切な計算を行う(現在の距離の総和から、色xの距離の総和を引いて、y\times(色xの本数)を足す)。vの子uに行くときは辺(v,u)の情報をmapに追加し、uから帰ってきたら辺(v,u)の情報を破棄すれば、全体を1つのmapで処理することができ、空間計算量がO(N+Q)で済むようになる。時間計算量はO((N+Q)\log{N})である。

実装

Submission #6307254 - AtCoder Beginner Contest 133

  • LCAはいざというときのために準備しておく。それ以外の実装も量はあるが、適正帯には難しくないと思う。dfsに従って情報を入れたり捨てたりするのがポイント。
  • 構造体を書くと安心感がある。