题解:P8294 [省选联考 2022] 最大权独立集问题

题意

给定一棵 \(n\) 个点的二叉树,点 \(i\) 的点权为 \(a_i\)。你需要将所有边依次删除,删除一条边 \((u,v)\) 会交换 \(a_u\)\(a_v\),并产生 \(a_u+a_v\) 的代价,求最小代价。\(1\leq n\leq 5\times 10^3\)

题解

构式题。

考虑到对于一棵子树 \(sub_x\),其能和子树外产生的联系只会是:操作 \((x,fa_x)\) 这条边,使得 \(x\) 上的点权 \(a_u\) 交换到子树外,\(fa_x\) 上的某个点权交换到子树内。设计这样的 DP 状态:对于 \(u,v\),设 \(x=\operatorname{lca}(u,v)\),则 \(f_{u,v}\) 表示考虑 \(sub_x\) 内的点,将 \(a_u\) 换到子树外,将换入的点权一路交换到 \(v\),要求把 \(sub_x\) 内所有边和 \((x,fa_x)\) 这条边删去的最小代价。注意我们在状态中不考虑换入点权产生的代价,可以理解为一种延后计算,在更浅的祖先处再去计算这个代价。

自底向上做树形 DP,DFS 到点 \(x\) 时计算所有 \(\operatorname{lca}(u,v)=x\)\(f_{u,v}\)

接下来是一些精神污染的部分,我们分类讨论 \(x\) 的儿子节点个数。

\(|son_x|=0\)

显然 \(f_{x,x}=a_x\)


\(|son_x|=1\)

此时有 \(u=x\)\(v=x\)。设 \(x\) 的儿子为 \(y\)

\(u=x\)

考虑 \(x\) 的邻边的操作先后顺序,容易发现是先操作 \((x,fa_x)\) 再操作 \((x,y)\)。枚举 \(i\) 使得 \(\operatorname{lca}(i,v)=y\)

\[f_{u,v}\gets\min\{f_{i,v}+a_u\} \]

\(v=x\)

此时是先操作 \((x,y)\) 再操作 \((x,fa_x)\)。枚举 \(i\) 使得 \(\operatorname{lca}(u,i)=y\),则操作完 \((x,y)\) 后,\(a_v\) 会作为 \(y\) 子树的换入点权,需要加上这部分贡献,有转移:

\[f_{u,v}\gets\min\{f_{u,i}+a_u+a_v(dep_i-dep_v)\} \]


\(|son_x|=2\)

\(u\neq x\land v\neq x\)

\(x\)\(u\) 方向上的儿子为 \(y_1\),另一侧的儿子为 \(y_2\)。此时是先操作 \((x,y_1)\),再操作 \((x,fa_x)\),最后再操作 \((x,y_2)\)。枚举 \(i,j\) 使得 \(\operatorname{lca}(u,i)=y_1,\operatorname{lca}(j,v)=y_2\),则操作完 \((x,y_1)\) 后,\(a_x\) 会作为 \(y_1\) 子树的换入点权,有转移:

\[f_{u,v}\gets \min\{f_{u,i}+f_{j,v}+a_x(dep_i-dep_x)+a_u\} \]

对于每个 \(u\),预处理出 \(\min\limits_i\{f_{u,i}+a_x(dep_i-dep_x)+a_u\}\),对于每个 \(v\),预处理出 \(\min\limits_j f_{j,v}\)。枚举 \(u,v\) 转移即可。

\(u=x\)

\(x\)\(v\) 方向上的儿子为 \(y_1\),另一侧的儿子为 \(y_2\)。此时是先操作 \((x,fa_x)\),再操作 \((x,y_1)\),最后再操作 \((x,y_2)\)。枚举 \(i,j,k\) 使得 \(\operatorname{lca}(i,v)=y_1,\operatorname{lca}(j,k)=y_2\),则操作完 \((x,y_1),(x,y_2)\) 后,\(a_i\) 会作为 \(y_2\) 子树的换入点权,有转移:

\[\begin{align*} f_{u,v}\gets&\min\{f_{i,v}+f_{j,k}+a_i(dep_k-dep_u)+a_u\}\\ =&\min_i\left\{f_{i,v}+\min_k\left\{a_i(dep_k-dep_u)+\min_j f_{j,k}\right\}\right\}+a_u \end{align*} \]

对于每个 \(k\),预处理出 \(h_k=\min\limits_j f_{j,k}\),再对每个 \(i\),预处理出 \(g_i=\min\limits_k\{a_i(dep_k-dep_u)+h_k\}\),枚举 \(u,v,i\) 转移即可。每对 \((j,k)\) 只会在 \(y_2\) 处被枚举到,每对 \((v,i)\) 只会在 \(y_1\) 处被枚举到,时间复杂度为 \(\mathcal{O}(n^2)\)

\(v=x\)

\(x\)\(u\) 方向上的儿子为 \(y_1\),另一侧的儿子为 \(y_2\)。此时是先操作 \((x,y_2)\),再操作 \((x,y_1)\),最后再操作 \((x,fa_x)\)。枚举 \(i,j,k\) 使得 \(\operatorname{lca}(u,i)=y_1,\operatorname{lca}(j,k)=y_2\),则操作完 \((x,y_2)\) 后,\(a_v\) 会作为 \(y_2\) 子树的换入点权,再操作 \((x,y_1)\) 后,\(a_j\) 会作为 \(y_1\) 子树的换入点权,有转移:

\[\begin{align*} f_{u,v}\gets&\min\{f_{u,i}+f_{j,k}+a_v(dep_k-dep_v)+a_j(dep_i-dep_v)+a_u\}\\ =&\min_i\left\{f_{u,i}+\min_j\left\{a_j(dep_i-dep_v)+\min_k\{f_{j,k}+a_v(dep_k-dep_v)\}\right\}\right\}+a_u \end{align*} \]

对于每个 \(j\),预处理出 \(h_j=\min\limits_k\{f_{j,k}+a_v(dep_k-dep_v)\}\),再对每个 \(i\),预处理出 \(g_i=\min\limits_j\{a_j(dep_i-dep_v)+h_j\}\),枚举 \(u,v,i\) 转移即可。时间复杂度为 \(\mathcal{O}(n^2)\)


考虑如何统计答案,还是分类讨论 \(1\) 号节点的儿子个数。

\(|son_1|=1\)

\(son_1=y\)。枚举 \(u,v\) 使得 \(\operatorname{lca}(u,v)=y\),则操作完 \((1,y)\)\(a_1\) 会作为 \(y\) 子树的换入点权:

\[ans\gets \min\{f_{u,v}+a_1(dep_v-dep_1)\} \]

\(|son_1|=2\)

\(son_1=\{y_1,y_2\}\),不妨钦定我们是先操作 \((1,y_1)\) 再操作 \((1,y_2)\)。枚举 \(i,j,k,l\) 使得 \(\operatorname{lca}(i,j)=y_1,\operatorname{lca}(k,l)=y_2\),则操作完 \((1,y_1)\) 后,\(a_1\) 会作为 \(y_1\) 子树的换入点权,再操作完 \((1,y_2)\) 后,\(a_i\) 会作为 \(y_2\) 子树的换入点权:

\[ans\gets \min\{f_{i,j}+f_{k,l}+a_1(dep_j-dep_1)+a_i(dep_l-dep_1)\} \]

对于每个 \(i\),预处理出 \(\min\limits_j\{f_{i,j}+a_1(dep_j-dep_1)\}\),对于每个 \(l\),预处理出 \(\min\limits_k f_{k,l}\),枚举 \(i,l\) 更新答案即可。


这样我们就 \(\mathcal{O}(n^2)\) 地解决了本题。

代码倒是不算难写。

主要代码
void dfs1(int x) {
	sub[x].push_back(x);
	for (int y : son[x]) {
		dep[y] = dep[x] + 1, dfs1(y);
		for (int z : sub[y]) sub[x].push_back(z);
	}
	for (int y : sub[x]) vec[x].push_back({x, y}), vec[x].push_back({y, x});
	if (son[x].size() == 2) {
		int y0 = son[x][0], y1 = son[x][1];
		for (int p : sub[y0]) for (int q : sub[y1])
			vec[x].push_back({p, q}), vec[x].push_back({q, p});
	}
}
void dfs2(int x) {
	if (son[x].empty()) return f[x][x] = a[x], void();
	for (auto y : son[x]) dfs2(y);
	if (son[x].size() == 1) {
		int s = son[x][0];
		for (auto [v, i] : vec[s]) {
			chk_min(f[x][v], f[i][v] + a[x]);
			int u = v;
			chk_min(f[u][x], f[u][i] + (ll)a[x] * (dep[i] - dep[x]) + a[u]);
		}
		return;
	}
	int y0 = son[x][0], y1 = son[x][1];
	for (int y : sub[x]) mn1[y] = mn2[y] = inf;
	for (auto [j, k] : vec[y0]) chk_min(mn1[k], f[j][k]);
	for (auto [j, k] : vec[y1]) chk_min(mn1[k], f[j][k]);
	for (auto [i, k] : vec[x]) if (i != x && k != x)
		chk_min(mn2[i], (ll)a[i] * (dep[k] - dep[x]) + mn1[k]);
	for (auto [v, i] : vec[y0]) chk_min(f[x][v], a[x] + f[i][v] + mn2[i]);
	for (auto [v, i] : vec[y1]) chk_min(f[x][v], a[x] + f[i][v] + mn2[i]);
	for (int y : sub[x]) mn1[y] = mn2[y] = inf;
	for (auto [j, k] : vec[y0]) chk_min(mn1[j], f[j][k] + (ll)a[x] * (dep[k] - dep[x]));
	for (auto [j, k] : vec[y1]) chk_min(mn1[j], f[j][k] + (ll)a[x] * (dep[k] - dep[x]));
	for (auto [i, j] : vec[x]) if (i != x && j != x)
		chk_min(mn2[i], (ll)a[j] * (dep[i] - dep[x]) + mn1[j]);
	for (auto [u, i] : vec[y0]) chk_min(f[u][x], a[u] + f[u][i] + mn2[i]);
	for (auto [u, k] : vec[y1]) chk_min(f[u][x], a[u] + f[u][k] + mn2[k]);
	for (int y : sub[x]) mn1[y] = mn2[y] = inf;
	for (auto [u, i] : vec[y0]) {
		chk_min(mn1[u], f[u][i] + (ll)a[x] * (dep[i] - dep[x]) + a[u]);
		int j = u, v = i;
		chk_min(mn2[v], f[j][v]);
	}
	for (auto [u, i] : vec[y1]) {
		chk_min(mn1[u], f[u][i] + (ll)a[x] * (dep[i] - dep[x]) + a[u]);
		int j = u, v = i;
		chk_min(mn2[v], f[j][v]);
	}
	for (auto [u, v] : vec[x]) if (u != x && v != x)
		chk_min(f[u][v], mn1[u] + mn2[v]);
}

int main() {
	ios::sync_with_stdio(0), cin.tie(0);
	cin >> n;
	for (int i = 1; i <= n; ++i) cin >> a[i];
	for (int i = 2; i <= n; ++i) cin >> fa[i], son[fa[i]].push_back(i);
	for (int i = 1; i <= n; ++i) fill(f[i] + 1, f[i] + n + 1, inf);
	dfs1(1);
	for (int y : son[1]) dfs2(y);
	if (son[1].size() == 1) {
		for (auto [u, v] : vec[son[1][0]])
			chk_min(ans, f[u][v] + (ll)a[1] * (dep[v] - dep[1]));
	} else {
		for (int y : sub[1]) mn1[y] = mn2[y] = inf;
		int y0 = son[1][0], y1 = son[1][1];
		for (auto [i, j] : vec[y0]) chk_min(mn1[i], f[i][j] + (ll)a[1] * (dep[j] - dep[1]));
		for (auto [k, l] : vec[y1]) chk_min(mn2[l], f[k][l]);
		for (auto [i, l] : vec[1]) if (i != 1 && l != 1)
			chk_min(ans, mn1[i] + mn2[l] + (ll)a[i] * (dep[l] - dep[1]));
		for (int y : sub[1]) mn1[y] = mn2[y] = inf;
		swap(y0, y1);
		for (auto [i, j] : vec[y0]) chk_min(mn1[i], f[i][j] + (ll)a[1] * (dep[j] - dep[1]));
		for (auto [k, l] : vec[y1]) chk_min(mn2[l], f[k][l]);
		for (auto [i, l] : vec[1]) if (i != 1 && l != 1)
			chk_min(ans, mn1[i] + mn2[l] + (ll)a[i] * (dep[l] - dep[1]));
	}
	cout << ans;
	return 0;
}
posted @ 2026-01-29 19:13  P2441M  阅读(4)  评论(0)    收藏  举报