CF1988D (思维 + 树形DP)
题意
有一棵包含
个结点的树。编号为 的结点上有一个攻击力为 的怪物。你要跟怪物对战若干回合,直至将它们全部杀死。
每一回合,所有存活着的怪物会先对你进行一次攻击,你损失的生命值是所有存活着的怪物的攻击力之和;然后,你选择若干结点,满足: 两两不在同一条边上,将 位置上的怪物杀死。
若选择最优的方案,在所有怪物被杀死之后,你最少损失多少生命值。
数据范围:
题解
首先,
考虑 树形DP
,设
结论 :最优策略下的总回合数一定小于等于
证明:对于任意一个结点
如果存在某一个怪兽,我们在第
有:
因为需要满足
DP 的转移方程:
答案即:
时间复杂度为
点击查看代码
#include <bits/stdc++.h>
using i64 = long long;
constexpr i64 inf = 1E18;
constexpr int N = 3E5 + 5;
i64 a[N], dp[N][30], ans;
std::vector <int> adj[N];
int n, u, v;
template <typename T>
inline void read(T &f) {
f = 0; T fu = 1; char c = getchar();
while (c < '0' || c > '9') { if (c == '-') { fu = -1; } c = getchar(); }
while (c >= '0' && c <= '9') { f = (f << 3) + (f << 1) + (c & 15); c = getchar(); }
f *= fu;
}
template <typename T>
void print(T x) {
if (x < 0) putchar('-'), x = -x;
if (x < 10) putchar(x + 48);
else print(x / 10), putchar(x % 10 + 48);
}
template <typename T>
void print(T x, char t) {
print(x); putchar(t);
}
void solve() {
read(n);
for (int i = 1; i <= n; i++) {
read(a[i]);
adj[i].clear();
}
for (int i = 1; i < n; i++) {
read(u); read(v);
adj[u].push_back(v);
adj[v].push_back(u);
}
const int M = std::log2(n) + 1;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= M; j++) {
dp[i][j] = 0;
}
}
auto dfs = [&](auto self, int u, int p) -> void {
for (int i = 1; i <= M; i++) {
dp[u][i] = a[u] * i;
}
for (auto v : adj[u]) {
if (v == p) {
continue;
}
self(self, v, u);
for (int i = 1; i <= M; i++) {
i64 min = inf;
for (int j = 1; j <= M; j++) {
if (j != i) {
min = std::min(min, dp[v][j]);
}
}
dp[u][i] += min;
}
}
};
dfs(dfs, 1, 0);
ans = inf;
for (int i = 1; i <= M; i++) {
ans = std::min(ans, dp[1][i]);
}
print(ans, '\n');
}
int main() {
int T;
read(T);
while (T--) {
solve();
}
return 0;
}
Bonus
在更新
点击查看代码
#include <bits/stdc++.h>
using i64 = long long;
constexpr i64 inf = 1E18;
constexpr int N = 3E5 + 5;
i64 a[N], dp[N][30], pre[30], suf[30], ans, min;
std::vector <int> adj[N];
int n, u, v;
template <typename T>
inline void read(T &f) {
f = 0; T fu = 1; char c = getchar();
while (c < '0' || c > '9') { if (c == '-') { fu = -1; } c = getchar(); }
while (c >= '0' && c <= '9') { f = (f << 3) + (f << 1) + (c & 15); c = getchar(); }
f *= fu;
}
template <typename T>
void print(T x) {
if (x < 0) putchar('-'), x = -x;
if (x < 10) putchar(x + 48);
else print(x / 10), putchar(x % 10 + 48);
}
template <typename T>
void print(T x, char t) {
print(x); putchar(t);
}
void solve() {
read(n);
for (int i = 1; i <= n; i++) {
read(a[i]);
adj[i].clear();
}
for (int i = 1; i < n; i++) {
read(u); read(v);
adj[u].push_back(v);
adj[v].push_back(u);
}
const int M = std::log2(n) + 1;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= M; j++) {
dp[i][j] = 0;
}
}
auto dfs = [&](auto self, int u, int p) -> void {
for (int i = 1; i <= M; i++) {
dp[u][i] = a[u] * i;
}
for (auto v : adj[u]) {
if (v == p) {
continue;
}
self(self, v, u);
pre[1] = dp[v][1]; suf[M] = dp[v][M];
for (int i = 2; i <= M; i++) {
pre[i] = std::min(pre[i - 1], dp[v][i]);
}
for (int i = M - 1; i >= 1; i--) {
suf[i] = std::min(suf[i + 1], dp[v][i]);
}
for (int i = 1; i <= M; i++) {
min = inf;
if (i - 1 >= 1) {
min = std::min(min, pre[i - 1]);
}
if (i + 1 <= M) {
min = std::min(min, suf[i + 1]);
}
dp[u][i] += min;
}
}
};
dfs(dfs, 1, 0);
ans = inf;
for (int i = 1; i <= M; i++) {
ans = std::min(ans, dp[1][i]);
}
print(ans, '\n');
}
int main() {
int T;
read(T);
while (T--) {
solve();
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】