好久没有学习新的知识了。
https://www.luogu.com.cn/problem/P2495
今天我学习了一下虚树。
虚树就是一个不存在的树。用来简化问题。它只包括一些关键点和他的LCA
我们来看看上面这一道题
题意
给出一棵树,含有边权。
每次询问给出k个点,然后询问每个点与根断开的最小代价。
做法
因为数据的关系肯定没法直接dp过去的。
首先我们可以发现可以与根到其他点的路径没有关系,所以只要保存询问点到根以及lca的信息就可以了。那么这就是虚树了。
关于虚树的构造,网上有很多博客写得不错,我就不讲了。
下面讲怎么虚树上处理dp
包括两种情况:
1.断开自己与父亲,代价为从根到该节点的最小值。
2.不考虑该点,子树内的所有询问点都断开的代价。
具体看代码吧。
#include "bits/stdc++.h"
using namespace std;
inline int read() {
int x = 0;
bool f = 1;
char c = getchar();
for (; !isdigit(c); c = getchar()) if (c == '-') f = 0;
for (; isdigit(c); c = getchar()) x = (x << 3) + (x << 1) + c - '0';
if (f) return x;
return 0 - x;
}
#define SZ(x) ((int)x.size())
#define ll long long
const int maxn = 250000 + 10;
struct edge {
int u, v, w, nxt;
} ed[maxn << 1];
int head[maxn << 1], cnt;
void add_e(int u, int v, int w) {
ed[++cnt] = edge{u, v, w, head[u]};
head[u] = cnt;
}
int top[maxn], id[maxn], f[maxn], dep[maxn], sz[maxn], son[maxn], rk[maxn];
ll mn[maxn];
void dfs1(int u, int fa, int d) {
f[u] = fa, dep[u] = d, sz[u] = 1;
for (int i = head[u]; i; i = ed[i].nxt) {
int v = ed[i].v;
if (v == fa) continue;
mn[v] = min(mn[u], 1ll * ed[i].w);
dfs1(v, u, d + 1);
sz[u] += sz[v];
if (sz[v] > sz[son[u]]) son[u] = v;
}
}
void dfs2(int u, int t) {
top[u] = t, id[u] = ++cnt, rk[cnt] = u;
if (!son[u]) return;
dfs2(son[u], t);
for (int i = head[u]; i; i = ed[i].nxt) {
int v = ed[i].v;
if (v != son[u] && v != f[u]) dfs2(v, v);
}
}
int LCA(int x, int y) {
while (top[x] != top[y]) {
if (dep[top[x]] < dep[top[y]]) swap(x, y);
x = f[top[x]];
}
if (dep[x] > dep[y]) return y;
return x;
}
bool cmp(int const &a, int const &b) {
return id[a] < id[b];
}
int n, m, k, a[maxn], s[maxn], st;
vector<int> g[maxn];
void build(int x) {
if (st == 1) {
s[++st] = x;
return;
}
int lca = LCA(x, s[st]);
if (lca == s[st]) return;
while (st > 1 && id[s[st - 1]] >= id[lca]) {
g[s[st - 1]].push_back(s[st]), st--;
}
if (lca != s[st]) g[lca].push_back(s[st]), s[st] = lca;
s[++st] = x;
}
ll solve(int u) {
if (SZ(g[u]) == 0) return mn[u];
ll sum = 0;
for (int v:g[u]) {
sum += solve(v);
}
g[u].clear();
return min(sum, mn[u]);
}
int main() {
mn[1] = 1ll << 60;
n = read();
for (int i = 1, u, v, w; i < n; i++) {
u = read();
v = read();
w = read();
add_e(u, v, w);
add_e(v, u, w);
}
dfs1(1, 0, 1);
cnt = 0;
dfs2(1, 1);
m = read();
while (m--) {
k = read();
for (int i = 1; i <= k; i++) a[i] = read();
sort(a + 1, a + k + 1, cmp);
s[st = 1] = 1;
for (int i = 1; i <= k; i++) build(a[i]);
while (st > 0) g[s[st - 1]].push_back(s[st]), st--;
cout << solve(1) << endl;
}
return 0;
}