虚树

虚树

1、虚树是将一个树的点集的某一个子集,以及该子集中点的 \(LCA\) 的集合,一起所重构出来的一棵树。和\(targen\) 一样都是缩点。

2、用到的方法是二次排序以及 + \(lca\) 连边,排序主要是根据遍历时的 \(dfs\) 序来排列的,小的排前面。

3、时间复杂度:\(O(nlogn)\),空间复杂度:\(O(nlogn)\)

4、模版:洛谷P2495

5、操作:

  • 截取关键点
  • 排序关键点,并且 \(lca\) 连边

如图:
image

image

image

image

image

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();
}
posted @ 2024-08-29 13:47  grape_king  阅读(23)  评论(0)    收藏  举报