[传送门]
如果只有单次询问,可以直接树形DP
$f\left[u\right]$表示以$u$为根的子树中所有资源丰富的岛屿不与$u$联通的最小代价
转移方程显然
若儿子节点$v$为资源丰富的岛屿
$f\left[u\right] = f\left[u\right] + w\left[u, v\right]$
若儿子节点$v$不是资源丰富的岛屿
$f\left[u\right] = f\left[u\right] + min\left(w\left[u, v\right], f\left[v\right]\right)$
这样下来时间复杂度是$O(n\times q)$。
但是可以发现,资源丰富的点的总和是与$n$同阶的,也就是询问很多的情况下,其实这些关键点是很少的,整棵树我们是没有必要遍历一遍的,那么就引入虚树(Virtual tree)这个概念。
其实就是把关键点和关键点之间的LCA保存下来,被删掉的点之间边的信息用某种方式保留下来,比如这道题中被删掉的点之间的边权,用取$min$的方式保存在保留下来的边中,因为若一条链上没有关键点,其叶子才是关键点,那么中间这条长链选择删哪条边都可以,为了使代价最小,显然删权值最小的边。
建树就是板子了。维护以dfs序为权值的单调栈,pop的过程连边,发现LCA未在栈里及时加进去即可。详见[OI Wiki - 虚树]
其实更多考的是树形DP吧。

#include <bits/stdc++.h>
#define ll long long
using namespace std;
template<typename T>
inline void read(T &x) {
x = 0; T f = 1; char ch = getchar();
while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); }
while (ch >= '0' && ch <= '9') { x = x * 10 + ch - 48; ch = getchar(); }
x *= f;
}
const int INF = 0x3f3f3f3f;
const int N = 250000 + 7;
const int sz = 18;
struct E {
int v, ne, c;
} e[N << 1];
int head[N], cnt, n, m, k;
int mn[N][19], fa[N][19], dep[N], st[N], top, h[N];
int dfn[N], id, edge;
ll f[N];
bool is[N];
void add(int u, int v, int c) {
e[++cnt].v = v; e[cnt].c = c; e[cnt].ne = head[u]; head[u] = cnt;
}
void dfs(int u, int pre) {
dep[u] = dep[pre] + 1, fa[u][0] = pre;
dfn[u] = ++id;
for (int i = 1; i <= sz; i++) {
fa[u][i] = fa[fa[u][i - 1]][i - 1];
mn[u][i] = min(mn[u][i - 1], mn[fa[u][i - 1]][i - 1]);
}
for (int i = head[u]; i; i = e[i].ne) {
int v = e[i].v;
if (v == pre) continue;
mn[v][0] = e[i].c;
dfs(v, u);
}
}
int Lca(int u, int v) {
edge = INF;
if (dep[u] < dep[v]) swap(u, v);
int dif = dep[u] - dep[v];
for (int i = sz; ~i; i--)
if (dif >> i & 1)
edge = min(mn[u][i], edge), u = fa[u][i];
if (u == v) return u;
for (int i = sz; ~i; i--)
if (fa[u][i] != fa[v][i]) {
edge = min(edge, min(mn[u][i], mn[v][i]));
u = fa[u][i], v = fa[v][i];
}
edge = min(edge, min(mn[u][0], mn[v][0]));
return fa[u][0];
}
inline bool cmp(const int &a, const int &b) { return dfn[a] < dfn[b]; }
void DP(int u) {
f[u] = 0;
for (int i = head[u]; i; i = e[i].ne) {
int v = e[i].v;
DP(v);
if (is[v])
f[u] += e[i].c;
else
f[u] += min((ll)e[i].c, f[v]);
}
}
int main() {
read(n);
for (int i = 1; i < n; i++) {
int u, v, c;
read(u), read(v), read(c);
add(u, v, c);
add(v, u, c);
}
dfs(1, 0);
read(m);
while (m--) {
read(k);
for (int i = 1; i <= k; i++) read(h[i]), is[h[i]] = 1;
sort(h + 1, h + k + 1, cmp);
st[top = 1] = 1, head[1] = 0, cnt = 0;
for (int i = 1; i <= k; i++) {
int lca = Lca(st[top], h[i]);
if (lca != st[top]) {
while (dfn[lca] < dfn[st[top - 1]]) {
Lca(st[top - 1], st[top]);
add(st[top - 1], st[top], edge);
top--;
}
if (dfn[lca] > dfn[st[top - 1]]) {
head[lca] = 0;
Lca(lca, st[top]);
add(lca, st[top], edge);
st[top] = lca;
} else {
Lca(lca, st[top]);
add(lca, st[top--], edge);
}
}
head[h[i]] = 0;
st[++top] = h[i];
}
for (int i = 1; i < top; i++)
Lca(st[i], st[i + 1]), add(st[i], st[i + 1], edge);
DP(1);
printf("%lld\n", f[1]);
for (int i = 1; i <= k; i++) is[h[i]] = 0;
}
return 0;
}
