题解: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}\),转移令
对于第二类操作,原状态中的 \(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;
}

浙公网安备 33010602011771号