luogu P4657 [CEOI2017]Chase
https://www.luogu.com.cn/problem/P4657
我们可以直接考虑贡献
逃亡者碰到过的铁球追随者一定会碰到
直接考虑追随者会多碰到的铁球即可
对于一条路径,如果在起始点放一个磁铁,那么它的贡献是周围一圈的点
如果在其他点放磁铁,那么它的贡献是周围一圈的点减去上一个点的权值
于是我们考虑DP
f [ u ] [ i ] f[u][i] f[u][i]表示从 u u u子树中的某个点出发(包括 u u u),最后到达 u u u的最大路径
g [ v ] [ i ] g[v][i] g[v][i]表示从 v v v出发,到达 v v v子树中某个点的最大路径
然后答案就是类似max卷积一样
正反都跑一次转移即可
code:
#include<bits/stdc++.h>
#define N 200050
#define ll long long
using namespace std;
struct edge {
int v, nxt;
} e[N << 1];
int p[N], eid;
void init() {
memset(p, -1, sizeof p);
eid = 0;
}
void insert(int u, int v) {
e[eid].v = v;
e[eid].nxt = p[u];
p[u] = eid ++;
}
int n, m, a[N], sta[N];
ll f[N][105], g[N][105], s[N], ans = 0;
void dfs(int u, int fa) {
for(int i = 1; i <= m; i ++) f[u][i] = s[u], g[u][i] = s[u] - a[fa];
for(int i = p[u]; i + 1; i = e[i].nxt) {
int v = e[i].v;
if(v == fa) continue;
dfs(v, u);
for(int j = 0; j <= m; j ++) ans = max(ans, f[u][j] + g[v][m - j]);
for(int j = 1; j <= m; j ++) {
f[u][j] = max(f[u][j], max(f[v][j], f[v][j - 1] + s[u] - a[v]));
g[u][j] = max(g[u][j], max(g[v][j], g[v][j - 1] + s[u] - a[fa]));
}
}
int top = 0;
for(int i = p[u]; i + 1; i = e[i].nxt) {
int v = e[i].v;
if(v == fa) continue;
sta[++ top] = v;
}
for(int i = 1; i <= m; i ++) f[u][i] = s[u], g[u][i] = s[u] - a[fa];
for(int i = top; i >= 1; i --) {
int v = sta[i];
for(int j = 0; j <= m; j ++) ans = max(ans, f[u][j] + g[v][m - j]);
for(int j = 1; j <= m; j ++) {
f[u][j] = max(f[u][j], max(f[v][j], f[v][j - 1] + s[u] - a[v]));
g[u][j] = max(g[u][j], max(g[v][j], g[v][j - 1] + s[u] - a[fa]));
}
}
}
int main() {
init();
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; i ++) scanf("%d", &a[i]);
for(int i = 1; i < n; i ++) {
int u, v;
scanf("%d%d", &u, &v);
insert(u, v), insert(v, u);
s[u] += a[v], s[v] += a[u];
}
dfs(1, 0);
printf("%lld", ans);
return 0;
}