P9021 [USACO23JAN] Subtree Activation P

Rock-N-Roll / 2024-10-13 / 原文

P9021 [USACO23JAN] Subtree Activation P

这种看上去就很不常规的东西不用想着怎么构造最佳方案,这条路一定是行不通的,考虑转化题意。

考虑变化的实质只有两种:全 \(0\) 状态和 \(x\) 子树全满的状态转化;\(x\) 子树全满和 \(y\) 子树全满的状态转化,其中 \(x,y\) 有边。这样的状态转化描述起来十分抽象,于是考虑形象化地建图描述。

于是考虑建新图 \(G\)。对于 \((x,y)\)\(G\) 上连边权为 \(|siz_x-siz_y|\) 的边,同时将每个 \(x\)\((0,x)\),边权为 \(siz_x\)。这样一来问题似乎转化为了在新图上找到一个欧拉图覆盖所有的点且使边权最小。

\(n\le 2\times 10^5\),考虑 \(O(n)\) 的做法,就只有树形 dp 了。在我们的描述中 \(0\) 号节点很关键,考虑删掉 \(0\) 号点后这个欧拉图形成了若干个联通块,这些个联通块都是链状的,只会有两个顶点可能与 \(0\) 相连,具体地,有 \(0\le p \le 2\) 条边和 \(0\) 号节点相连,通过 \(0\) 首尾相接。于是它设进 dp 状态中。具体地,设 \(dp_{x,0/1/2}\) 表示删掉 \(x\) 后所在联通块两个顶点有 \(0/1/2\) 个和 \(0\) 号点相连的最小花费,令 \(z=siz_x-siz_y\),初始情况显然是 \(dp_{x,0}=0,dp_{x,1}=siz_x,dp_{x,2}=2\times siz_x\)。方程是容易列出的:

\[\begin{gather*} dp_{x,0}\leftarrow \min\{dp_{x,0}+dp_{y,0}+2z,dp_{x,0}+dp_{y,2}\}\\ dp_{x,1}\leftarrow \min\{dp_{x,1}+dp_{y,0}+2z,dp_{x,0}+dp_{y,1}+z,dp_{x,1}+dp_{y,2}\}\\ dp_{x,2}\leftarrow \min\{dp_{x,2}+dp_{y,0}+2z,dp_{x,0}+dp_{y,2}+2z,dp_{x,1}+dp_{y,1}+z,dp_{x,2}+dp_{y,2}\} \end{gather*} \]

所求显然是 \(dp_{1,2}\)

代码:

#include <bits/stdc++.h>
#define N 200005
using namespace std;
int n;
struct Node {
	int to, nxt;
} e[N];
int head[N], cnt;
void add(int u, int v) {
	e[++cnt].to = v;
	e[cnt].nxt = head[u];
	head[u] = cnt;
}
int dp[N][3];
int siz[N];
void dfs(int x) {
	siz[x] = 1;
	for (int i = head[x]; i; i = e[i].nxt) {
		int y = e[i].to;
		dfs(y);
		siz[x] += siz[y];
	}
	dp[x][0] = 0, dp[x][1] = siz[x], dp[x][2] = 2 * siz[x];
	for (int i = head[x]; i; i = e[i].nxt) {
		int y = e[i].to;
		int z = siz[x] - siz[y];
		int tmp[3];
		tmp[0] = min(dp[x][0] + dp[y][0] + 2 * z, dp[x][0] + dp[y][2]);
		tmp[1] = min({dp[x][1] + dp[y][0] + 2 * z, dp[x][0] + dp[y][1] + z, dp[x][1] + dp[y][2]});
		tmp[2] = min({dp[x][2] + dp[y][0] + 2 * z, dp[x][0] + dp[y][2] + 2 * z, dp[x][1] + dp[y][1] + z, dp[x][2] + dp[y][2]});
		for (int j = 0; j < 3; j++)
			dp[x][j] = tmp[j];
	}
}
int main() {
	cin >> n;
	for (int i = 2, x; i <= n; i++)
		scanf("%d", &x), add(x, i);
	dfs(1);
	cout << dp[1][2] << "\n";
	return 0;
}