虚树
虚树
1、虚树是将一个树的点集的某一个子集,以及该子集中点的 \(LCA\) 的集合,一起所重构出来的一棵树。和\(targen\) 一样都是缩点。
2、用到的方法是二次排序以及 + \(lca\) 连边,排序主要是根据遍历时的 \(dfs\) 序来排列的,小的排前面。
3、时间复杂度:\(O(nlogn)\),空间复杂度:\(O(nlogn)\)。
4、模版:洛谷P2495。
5、操作:
- 截取关键点
- 排序关键点,并且 \(lca\) 连边
如图:
template<typename T>
struct bei_lca {
struct node {
int to, nxt;
T w;
node(int to = 0, int nxt = 0, T w = 0) : to(to), nxt(nxt), w(w) {}
};
static const int MX = 21;
int n, m, cnt, root, tot;
vector<int> dep, head, dfn;
vector<node> edg;
vector<array<int, MX>> fa;// 不同距离的祖先
vector<array<T, MX>> cost;// 该路径边权值之和
bei_lca() {}
bei_lca(int n_, int m_, int root_ = 1) : dep(n_ + 1), head(n_ + 1), dfn(n_ + 1), edg(m_ << 1 | 1), fa(n_ + 1), cost(n_ + 1) {
cnt = 0;
tot = 0;
n = n_;
m = m_;
root = root_;
for (int i = 0; i <= n_; i++) {
for (int j = 0; j < MX; j++) {
cost[i][j] = MOF;
}
}
}
inline void add(int u, int v, const T &w) {
++cnt;
edg[cnt].to = v;
edg[cnt].nxt = head[u];
edg[cnt].w = w;
head[u] = cnt;
}
inline void init(int n_, int m_, int root_ = 1) {
cnt = 0;
tot = 0;
n = n_;
m = m_;
root = root_;
dep.assign(n_ + 1, 0);
head.assign(n_ + 1, 0);
dfn.assign(n_ + 1, 0);
edg.assign(m_ << 1 | 1, node());
fa.assign(n_ + 1, array<int, MX>());
cost.assign(n_ + 1, array<T, MX>());
for (int i = 0; i <= n_; i++) {
for (int j = 0; j < MX; j++) {
cost[i][j] = MOF;
}
}
}
inline void dfs(int u, int pa) {
fa[u][0] = pa;
dfn[u] = ++tot;
dep[u] = dep[pa] + 1;
for (int i = 1; i < MX; i++) {
fa[u][i] = fa[fa[u][i - 1]][i - 1];
cost[u][i] = min(cost[fa[u][i - 1]][i - 1], cost[u][i - 1]);
}
for (int i = head[u]; i; i = edg[i].nxt) {
int v = edg[i].to;
T w = edg[i].w;
if (v == pa) {
continue;
}
cost[v][0] = w;
dfs(v, u);
}
}
int lca(int x, int y) {
if (dep[x] < dep[y]) {
swap(x, y);
}
int depth = dep[x] - dep[y];
for (int i = 0; depth; ++i, depth >>= 1) {
if (depth & 1) {
x = fa[x][i];
}
}
if (x == y) {
return x;
}
for (int i = MX - 1; i >= 0; i--) {
if (fa[x][i] != fa[y][i]) {
x = fa[x][i];
y = fa[y][i];
}
}
x = fa[x][0];
y = fa[y][0]; // 公共祖先
return x;
}
T lca_w(int x, int y) {
if (dep[x] < dep[y]) {
swap(x, y);
}
int depth = dep[x] - dep[y];
T ans = MOF;
for (int i = 0; depth; ++i, depth >>= 1) {
if (depth & 1) {
ans = min(ans, cost[x][i]);
x = fa[x][i];
}
}
if (x == y) {
return ans;
}
for (int i = MX - 1; i >= 0; i--) {
if (fa[x][i] != fa[y][i]) {
ans = min({ans, cost[x][i], cost[y][i]});
x = fa[x][i];
y = fa[y][i];
}
}
ans = min({ans, cost[x][0], cost[y][0]});
x = fa[x][0];
y = fa[y][0]; // 公共祖先
return ans;
}
};
template<typename T>
struct Xu_tree {
struct node {
int to, nxt;
T w;
node(int to = 0, int nxt = 0, T w = 0) : to(to), nxt(nxt), w(w) {}
};
int n, m, root, cnt;
vector<node> edg;
vector<int> head, vis;
vector<T> dp;
bei_lca<T> be;
Xu_tree() {}
Xu_tree(int n_, int m_, int root_ = 1) : edg(m_ << 1 | 1), head(n_ + 1), vis(n_ + 1), dp(n_ + 1) {
n = n_;
m = m_;
root = root_;
cnt = 0;
be = bei_lca<T>(n_, m_, root_);
}
inline void add(int u, int v, const T &w) {
++cnt;
edg[cnt].to = v;
edg[cnt].nxt = head[u];
edg[cnt].w = w;
head[u] = cnt;
}
inline void init(int n_, int m_, int root_ = 1) {
n = n_;
m = m_;
root = root_;
cnt = 0;
be = bei_lca<T>(n_, m_, root_);
edg.assign(m_ << 1 | 1, node());
head.assign(n_ + 1, 0);
vis.assign(n_ + 1, 0);
dp.assign(n_ + 1, 0);
}
inline void dfs(int u, int fa) {
T sum = 0;
for (int i = head[u]; i; i = edg[i].nxt) {
int v = edg[i].to;
T w = edg[i].w;
if (v == fa) {
continue;
}
dfs(v, u);
if (vis[v]) {
sum += w;
} else {
sum += min(dp[v], w);
}
}
dp[u] = sum;
}
inline void deal() {
int q;
std::cin >> q;
for (int i = 1; i <= q; i++) {
int k;
std::cin >> k;
vector<int> ver(k), st;
for (int j = 0; j < k; j++) {
std::cin >> ver[j];
vis[ver[j]] = 1;
st.push_back(ver[j]);
}
sort(ver.begin(), ver.end(), [&](int x, int y) {// 一次排序,以dfn数组排序,小的排前面
return be.dfn[x] < be.dfn[y];
});
for (int j = 1; j < k; j++) {
st.push_back(be.lca(ver[j - 1], ver[j]));// 加入相邻两个节点的lca
}
st.push_back(root);
sort(st.begin(), st.end(), [&](int x, int y) {// 二次排序,同上
return be.dfn[x] < be.dfn[y];
});
st.erase(unique(st.begin(), st.end()), st.end());// 去个重
for (int i = 1; i < st.size(); i++) {// 连边
int x = st[i - 1], y = st[i];
x = be.lca(x, y);// 得到的x一直是st数组中的其中一个节点
T w = be.lca_w(x, y);
add(x, y, w);
add(y, x, w);
}
dfs(root, 0);// 维护新的dp数组
std::cout << dp[1] << '\n';
// 将所有用到的东西初始化,以给下次利用
for (int i = 1; i <= cnt; i++) {
edg[i] = node(0, 0, 0);
}
cnt = 0;
for (auto p : st) {
head[p] = 0;
vis[p] = 0;
}
}
}
};
void solve() {
int n;
std::cin >> n;
Xu_tree<i64> tr(n, n - 1);
for (int i = 1, u, v; i < n; i++) {
i64 w;
cin >> u >> v >> w;
tr.be.add(u, v, w);
tr.be.add(v, u, w);
}
tr.be.dfs(1, 0);
tr.deal();
}

浙公网安备 33010602011771号