Loading

:D 获取中...

Solution -「CEOI2017」MUSEUM

因为网上的题解只有两篇且都是在 CSDN 垃圾桶里的并且写得十分冗长,这里更新一发好写的题解。

题目描述应该很像背包,需要选 \(k\) 个点出来,但是稍有不同的是这里并不仅仅局限于走子树,也可以走回来。于是直接把这种情况也设出来。定义 \(f_{i, j, 0 / 1}\) 表示现在在以 \(i\) 为根的子树中,已经走了 \(j\) 个点,\(0\) 表示没有回到 \(i\) 点,\(1\) 表示回到了 \(i\) 点。这样 01 联动就能更新出来想要的了。

状态转移。其实是用类似于树上背包的东西更新的,\(v\)\(u\) 的儿子。所以 \(f_{u, i, 0}\) 会表示的是目前更新到的 \(u\) 的子树部分,\(f_{v, j, 0}\) 则是即将更新的部分。对于 \(f_{u, i + j, 0}\),要么先走 \(u\) 部分的子树并回到 \(u\),再走 \(u \to v\),最后走以 \(v\) 为根的子树不用回来;要么先走 \(u \to v\),走以 \(v\) 为根的子树并回来,再走 \(v \to u\),最后走 \(u\) 部分子树不用回来。\(f_{u, i + j, 1}\) 要回来,那么就是 \(u\) 部分子树走回来以及以 \(v\) 为根的子树走回来,加上 \(u \to v\)\(v \to u\)

普通的树上背包更新方式保证 \(\mathcal O(n ^ 2)\)

方程懒得打,直接看代码。

namespace liuzimingc {
const int N = 1e4 + 5;

int n, k, x, f[N][N][2], siz[N];
vector<pair<int, int>> e[N];

void dfs(int u, int fa) {
	f[u][0][0] = f[u][0][1] = f[u][1][0] = f[u][1][1] = 0;
	siz[u] = 1;
	for (const auto &i : e[u]) {
		int v = i.first, w = i.second;
		if (v == fa) continue;
		dfs(v, u);
		for (int i = siz[u]; ~i; i--)
			for (int j = min(siz[v], k - i); j >= 0; j--) {
				f[u][i + j][0] = min(f[u][i + j][0], f[u][i][1] + w + f[v][j][0]);
				f[u][i + j][0] = min(f[u][i + j][0], f[u][i][0] + w * 2 + f[v][j][1]);
				f[u][i + j][1] = min(f[u][i + j][1], f[u][i][1] + w * 2 + f[v][j][1]);
			}
		siz[u] += siz[v]; // 普通的树上背包更新方式保证 O(n ^ 2)
	}
}

int main() {
	ios::sync_with_stdio(false);
	cin.tie(0); cout.tie(0);
	cin >> n >> k >> x;
	for (int i = 1; i < n; i++) {
		int u, v, w;
		cin >> u >> v >> w;
		e[u].push_back(make_pair(v, w));
		e[v].push_back(make_pair(u, w));
	}
	memset(f, 0x3f, sizeof(f));
	dfs(x, 0);
	cout << min(f[x][k][0], f[x][k][1]) << endl;
	return 0;
}
} // namespace liuzimingc
posted @ 2024-06-25 11:35  liuzimingc  阅读(7)  评论(0编辑  收藏  举报