题目大意:有一棵$n$个点的带边权树,第$i$个点有两个值$w_i,d_i$,表示在这个点做标记的代价为$w_i$,且这个点距离$d_i$以内至少要有一个点被标记,为最小代价。$n\leqslant6000$
题解:记$f[i][j]$表示以$i$为根的子树全部满足条件,且第$i$个点是由于$j$被标记导致的,$g[i]$表示以$i$为根的子树全部满足条件的代价。$f[i][j]=w_i+\min\limits_{v\in son[u]}\{f[v][j]-w_i,g[v]\}$
卡点:无
C++ Code:
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> const int maxn = 6e3 + 10, inf = 0x3f3f3f3f; int n, w[maxn], d[maxn]; int head[maxn], cnt; struct Edge { int to, nxt, w; } e[maxn << 1]; void addedge(int a, int b, int c) { e[++cnt] = (Edge) { b, head[a], c }; head[a] = cnt; e[++cnt] = (Edge) { a, head[b], c }; head[b] = cnt; } int dis[maxn][maxn], f[maxn][maxn], g[maxn]; void dfs0(int *dis, int u, int fa = 0) { for (int i = head[u], v; i; i = e[i].nxt) { v = e[i].to; if (v != fa) { dis[v] = dis[u] + e[i].w; dfs0(dis, v, u); } } } void dfs(int u, int fa = 0) { for (int i = head[u]; i; i = e[i].nxt) if (e[i].to != fa) dfs(e[i].to, u); for (int i = 1; i <= n; ++i) if (dis[u][i] <= d[u]) { f[u][i] = w[i]; for (int j = head[u], v; j; j = e[j].nxt) { v = e[j].to; if (v != fa) f[u][i] += std::min(f[v][i] - w[i], g[v]); } g[u] = std::min(g[u], f[u][i]); } } int main() { std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0); std::cin >> n; for (int i = 1; i <= n; ++i) std::cin >> w[i]; for (int i = 1; i <= n; ++i) std::cin >> d[i]; for (int i = 1, a, b, c; i < n; ++i) { std::cin >> a >> b >> c; addedge(a, b, c); } for (int i = 1; i <= n; ++i) dfs0(dis[i], i); memset(f, 0x3f, sizeof f), memset(g, 0x3f, sizeof g); dfs(1); std::cout << g[1] << '\n'; return 0; }