P9021 [USACO23JAN] Subtree Activation P

P9021 [USACO23JAN] Subtree Activation P

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

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

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

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

dpx,0min{dpx,0+dpy,0+2z,dpx,0+dpy,2}dpx,1min{dpx,1+dpy,0+2z,dpx,0+dpy,1+z,dpx,1+dpy,2}dpx,2min{dpx,2+dpy,0+2z,dpx,0+dpy,2+2z,dpx,1+dpy,1+z,dpx,2+dpy,2}

所求显然是 dp1,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;
}
posted @   长安19路  阅读(6)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示