AGC001 C - Shorten Diameter
新環境になってWordにためてた精進記録が取れなくなったのでブログで代用
これからは単発も上げるわ(虚空に向けて)
https://atcoder.jp/contests/agc001/tasks/agc001_c
問題:n頂点の木が与えられる。頂点を削除して直径をk以下にせよ。最小頂点数を求めよ
考察:
いろいろ考えると簡単には求まらないことがわかります。
根付き木に帰着して木DPをします。
いつも思うんですけどDPが生えるときって、あんまり論理的に考えていない気がします。経験上DPっぽい、みたいな程度になってますね。
さて、どういうDPをするかです。
dp[i][j]:iの部分木で、iの親から葉の距離がj以下になるために、削除する頂点の最小数
こんな感じです。
迷うところは、距離j以下か削除する頂点xだと思います。マージするとき明らかに前者のほうが便利なのでそうしました。
木DPなのでdfsして根付き木を構築しながらDPをします。
二乗の木DPなので計算量はO(N^2)です
repr(i,child[v],-1){
repr(j,child[u],-1){
マージする
}}
child[v]+=child[u];
大体こんな感じです。childで抑えて最後に更新することに注意
マージとしてはi+j-1>kになる場合、くっつつけられません。なので全て切り離し+child[u]です
そうでない場合は、すべて切り離すか、くっつつけるかの2通りで更新します。
j+1>iのとき最大深さが変わるので添え字が変化します。
最後に解を求めます。
任意の頂点について非連結(削除される)可能性を持っているのですべて調べる必要があります。
dp[i][j]:i=1~n,j=0~k+1で
n-child[i]+dp[i][j]です
部分木以外を削除し、部分木内でk以下になるように削除したものです。
提出コード
https://atcoder.jp/contests/agc001/submissions/7015773
この計算量で27msはちょっと信じがたいですね。ケース弱いのかも。