题解:P9967 [THUPC 2024 初赛] 采矿

难点全在读题,本质简单树形 DP 题。

将机器人所在点删去后,整棵树分裂成至多 \(3\) 个连通块,每个人只能在连通块内移动。首先猜测一个连通块内的人可以调整成任意合法状态。

证明

使用归纳法。选取连通块的一个叶子节点 \(u\),只需考虑初态和终态中 \(u\) 状态不同的情况。若 \(u\) 在初态中没人,终态中有人,选取离 \(u\) 最近的人移动到 \(u\) 即可;否则,选取离 \(u\) 最近的空点,将 \(u\) 到空点路径上的人逐个移动到空位上即可。这样调整后 \(u\) 的就变成了终态中的状态,删去 \(u\) 继续归纳即可。\(\Box\)

考虑 DP,令 \(dp_{u,lc,rc}\) 表示机器人位于点 \(u\),\(sub_{ls_u}\) 中有 \(lc\) 个人,\(sub_{rs_u}\) 中有 \(rc\) 个人,此时能获得的最大收益。显然总状态数是 \(\mathcal{O}(n^2)\) 的。再记录当前总人数 \(tot\) 即可得到 \(sub_u\) 外的人数 \(tot-lc-rc\)。

对于第一类操作,原状态中的 \(lc+rc\) 成为新状态中的 \(lc/rc\)。处理出 \(f_{u,i}=\max\limits_{lc+rc=i}dp_{u,lc,rc}\),转移令

\[dp_{u,lc,rc}\gets\max\begin{cases} f_{ls_u,lc}&\text{if }tot-lc<n-sz_{ls_u}\\ f_{rs_u,rc}&\text{if }tot-rc<n-sz_{rs_u} \end{cases} \]

对于第二类操作,原状态中的 \(lc/rc\) 成为新状态中的 \(lc+rc\)。处理出 \(g_{u,i,0}=\max\limits_{j}dp_{u,i,j}\) 和 \(g_{u,i,1}=\max\limits_jdp_{u,j,i}\),转移时令 \(dp_{u,lc,rc}\gets g_{fa_u,lc+rc,[u=rs_{fa_u}]}\) 即可。

对于第三类操作,令 \(tot\gets tot+1\),然后将 \(u=1\) 或者 \(tot-lc-rc>n-sz_u\) 的状态清空即可。

对于第四类操作,将 \(u=1\) 或者 \(lc+rc=tot\) 的状态清空,然后令 \(tot\gets tot-1\) 即可。

提前预处理出 \(mx_{u,i,0}\) 和 \(mx_{u,i,1}\) 分别表示 \(sub_u\) 内和 \(sub_u\) 外 \(p\) 的前 \(i\) 大之和。这部分可以做到 \(\mathcal{O}(n^2)/\mathcal{O}(n^2\log{n})\)。然后每次操作后令 \(dp_{u,lc,rc}\gets dp_{u,lc,rc}+mx_{ls_u,lc,0}+mx_{rs_u,rc,0}+mx_{u,tot-lc-rc,1}\) 即可。

时间复杂度为 \(\mathcal{O}(qn^2)\) 或者 \(\mathcal{O}(qn^2+n^2\log{n})\)。

代码
#include <bits/stdc++.h>

using namespace std;

using ll = long long;
using i128 = __int128;
using ui = unsigned int;
using ull = unsigned long long;
using u128 = unsigned __int128;
using ld = long double;
using pii = pair<int, int>;
const int MAXN = 305;
const ll inf = 1e18;

template<typename T> T lowbit(T x) { return x & -x; }
template<typename T> void chkMin(T &x, T y) { x = y < x ? y : x; }
template<typename T> void chkMax(T &x, T y) { x = x < y ? y : x; }
constexpr int lg2(ll x) { return 63 ^ __builtin_clzll(x); }
constexpr ll bitCeil(ll x) { return x == 1 ? 1ll : 1ll << lg2(x - 1) + 1; }

int n, q, s, tot;
int fa[MAXN], r[MAXN], p[MAXN];
int ls[MAXN], rs[MAXN], sz[MAXN];
vector<int> sub[MAXN];
vector<ll> mx1[MAXN], mx2[MAXN], f[MAXN], g[MAXN][2];
vector<vector<ll>> dp[MAXN];

void solve1() {
	for (int u = n; u; --u) {
		fill(f[u].begin(), f[u].end(), -inf);
		for (int i = 0; i <= sz[ls[u]]; ++i) {
			for (int j = 0; j <= sz[rs[u]]; ++j) {
				chkMax(f[u][i + j], dp[u][i][j]);
			}
		}
		for (int i = 0; i <= sz[ls[u]]; ++i) {
			for (int j = 0; j <= sz[rs[u]]; ++j) {
				dp[u][i][j] = -inf;
				if (i + j > tot) break;
				if (tot - i - j > n - sz[u]) continue;
				if (ls[u] && tot - i < n - sz[ls[u]]) chkMax(dp[u][i][j], f[ls[u]][i]);
				if (rs[u] && tot - j < n - sz[rs[u]]) chkMax(dp[u][i][j], f[rs[u]][j]);
				chkMax(f[u][i + j], dp[u][i][j]);
			}
		}
	}
}

void solve2() {
	for (int u = 1; u <= n; ++u) {
		fill(g[u][0].begin(), g[u][0].end(), -inf);
		fill(g[u][1].begin(), g[u][1].end(), -inf);
		for (int i = 0; i <= sz[ls[u]]; ++i) {
			for (int j = 0; j <= sz[rs[u]]; ++j) {
				chkMax(g[u][0][i], dp[u][i][j]);
				chkMax(g[u][1][j], dp[u][i][j]);
			}
		}
		for (int i = 0; i <= sz[ls[u]]; ++i) {
			for (int j = 0; j <= sz[rs[u]]; ++j) {
				dp[u][i][j] = -inf;
				if (fa[u] && i + j <= tot && tot - i - j <= n - sz[u]) dp[u][i][j] = g[fa[u]][u == rs[fa[u]]][i + j];
				chkMax(g[u][0][i], dp[u][i][j]);
                chkMax(g[u][1][j], dp[u][i][j]);
			}
		}
	}
}

void solve3() {
	++tot;
	for (int u = 1; u <= n; ++u) {
		for (int i = 0; i <= sz[ls[u]]; ++i) {
			for (int j = 0; j <= sz[rs[u]]; ++j) {
				if (u == 1 || tot - i - j > n - sz[u]) dp[u][i][j] = -inf;
			}
		}
	}
}

void solve4() {
	for (int u = 1; u <= n; ++u) {
		if (u == 1) {
			for (int i = 0; i <= sz[ls[u]]; ++i) {
				for (int j = 0; j <= sz[rs[u]]; ++j) {
					dp[u][i][j] = -inf;
				}
			}
			continue;
		}
		for (int i = max(tot - sz[rs[u]], 0); i <= min(sz[ls[u]], tot); ++i) {
			dp[u][i][tot - i] = -inf;
		}
	}
	--tot;
}


int main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr);
	
	cin >> n >> q >> s;
	for (int i = 2; i <= n; ++i) {
		cin >> fa[i];
		if (!ls[fa[i]]) ls[fa[i]] = i;
		else rs[fa[i]] = i;
	}
	for (int i = 2; i <= n; ++i) cin >> r[i];
	for (int i = 2; i <= n; ++i) cin >> p[i];

	for (int u = n; u; --u) {
		++sz[u];
		vector<ll> vec(sz[rs[u]] + 1, -inf);
		dp[u].assign(sz[ls[u]] + 1, vec);
		f[u].assign(sz[u] + 1, -inf);
		g[u][0].assign(sz[ls[u]] + 1, -inf);
		g[u][1].assign(sz[rs[u]] + 1, -inf);
		if (u != 1) sz[fa[u]] += sz[u];
	}
	dp[s][0][0] = 0;

	for (int u = 2; u <= n; ++u) {
		vector<int> vis(n + 1, 0);
		auto dfs = [&](auto &&self, int v) -> void {
			vis[v] = true;
			mx1[u].emplace_back(p[v]);
			if (ls[v]) self(self, ls[v]);
			if (rs[v]) self(self, rs[v]);
		};

		mx1[u] = mx2[u] = {0};
		dfs(dfs, u);
		for (int i = 1; i <= n; ++i) {
			if (!vis[i]) mx2[u].emplace_back(p[i]);
		}

		sort(mx1[u].begin() + 1, mx1[u].end(), greater<>());
		sort(mx2[u].begin() + 1, mx2[u].end(), greater<>());
		for (int i = 1; i < mx1[u].size(); ++i) mx1[u][i] += mx1[u][i - 1];
		for (int i = 1; i < mx2[u].size(); ++i) mx2[u][i] += mx2[u][i - 1];
	}

	while (q--) {
		int tp;
		cin >> tp;

		if (tp == 1) solve1();
		else if (tp == 2) solve2();
		else if (tp == 3) solve3();
		else solve4();

		for (int u = 1; u <= n; ++u) {
			for (int i = 0; i <= sz[ls[u]]; ++i) {
				for (int j = 0; j <= sz[rs[u]]; ++j) {
					if (dp[u][i][j] != -inf) {
						if (ls[u]) dp[u][i][j] += mx1[ls[u]][i];
						if (rs[u]) dp[u][i][j] += mx1[rs[u]][j];
						if (u != 1) dp[u][i][j] += mx2[u][tot - i - j];
						dp[u][i][j] += r[u];
					}
				}
			}
		}
	}

	ll ans = -inf;
	for (int u = 1; u <= n; ++u) {
		for (int i = 0; i <= sz[ls[u]]; ++i) {
			for (int j = 0; j <= sz[rs[u]]; ++j) {
				chkMax(ans, dp[u][i][j]);
			}
		}
	}
	
	if (ans == -inf) cout << "No solution.";
	else cout << ans;
	return 0;
}
posted @ 2026-09-25 12:36  P2441M  阅读(4)  评论(0)    收藏  举报