G - Erase Leaves 解説
by
MMNMM
頂点 \(1\) を削除した直後に残っている頂点から \(1\) つ選び(存在しなければ頂点 \(1\) を選び)、もとの木においてその頂点を根とした根つき木を考えます。
すると、頂点 \(1\) を削除する直前には、頂点 \(1\) の子孫がすべて削除されている必要があります。
逆に、頂点 \(1\) の子孫を深い方から削除していくと頂点 \(1\) およびその子孫以外の頂点を消すことなく目標を達成できるので、この条件のもとで必要な操作の回数は \(1\) を根とする部分木の大きさと等しいです。
よって、この問題は、もとの木において根にする頂点を動かしたときの「頂点 \(1\) を根とする部分木の大きさ」の最小値を求める問題に言い換えられます(同様に深い方から操作することで、好きな頂点を選んでその頂点を残すことができることがわかります)。
もとの木から頂点 \(1\) と頂点 \(1\) に接続する辺を取り除くと、森ができます。 そして(頂点 \(1\) でない)ある頂点を根としたときの、頂点 \(1\) を根とする部分木の大きさは、\(n\) から \(\left(\right.\)根として選んだ頂点が属する連結成分の大きさ\(\left.\right)\) を引いたものになります。
よって、求める最小値は \(n\) から森の連結成分の大きさのうち最大のものを引いたものになります。
これを実装することで、この問題を解くことができます。 実装には、頂点 \(1\) を根として木 DP を行ったり、Union Find などを用いたりすることができます。
実装例は以下のようになります。
#include <iostream>
#include <ranges>
#include <atcoder/dsu>
int main() {
using namespace std;
unsigned N;
cin >> N;
// 頂点 1 を取り除いた森について、
// union find で連結成分を管理
atcoder::dsu forest(N);
for (unsigned i = 1; i < N; ++i) {
unsigned u, v;
cin >> u >> v;
if (u != 1) // 頂点 1 に接続する辺でなければ
forest.merge(u - 1, v - 1); // 辺を追加
}
// N から最大の連結成分のサイズを引いたものが答え
cout << N - ranges::max(forest.groups() | views::transform([](auto &&g) { return size(g); })) << endl;
return 0;
}
投稿日時:
最終更新: