D - Minimum Steiner Tree Editorial
by
MMNMM
木 DP を用いることでもこの問題を解くことができます。
指定された頂点のひとつを根として選び、根付き木とします。 このとき、答えとなる部分グラフで頂点 \(v\) を使うことの必要十分条件は、\(v\) を親とする部分木の中に指定された頂点が存在することです。
\(v\) に対するこの条件が成り立つかどうかは、\(v\) の子に対する答えをすべて求められれば確かめられます。
あとは、DFS などと共に木 DP を行うことでこの問題を解くことができます。
実装例は以下のようになります。
#include <iostream>
#include <vector>
#include <algorithm>
int main() {
using namespace std;
unsigned N, K;
cin >> N >> K;
vector<vector<unsigned>> edges(N);
for(unsigned i{1}; i < N; ++i){
unsigned A, B;
cin >> A >> B;
edges[--A].emplace_back(--B);
edges[B].emplace_back(A);
}
// use[i] := 答えとなる木で頂点 i を使う
vector<bool> use(N);
// 木 DP をするための根
unsigned root;
cin >> root;
--root;
use[root] = true;
for(unsigned i{1}; i < K; ++i){
unsigned V;
cin >> V;
use[--V] = true;
}
// 木 DP を行う
// dfs(now, prev) := prev を親とした now 以下の部分木に使う頂点があるか
[dfs{[&edges, &use](const auto& f, unsigned now, unsigned prev) -> bool {
for(const auto next : edges[now])
if (next != prev)
use[now] = use[now] | f(f, next, now);
return use[now];
}}](unsigned v){
dfs(dfs, v, v);
}(root); // 使う頂点を根として木 DP
cout << ranges::count(use, true) << endl;
return 0;
}
posted:
last update:
