Solution
虽然这题加了很多边,但本质上还是一棵树,我们只需要维护一下链上的和与最大值,然后直接树形DP
但不过这道题的DP比较独特,因为一个节点的DP值可以从它的父亲更新过来。那么这个DP是不是就是有后向性了呢?
其实我们只需要DP两遍就可以了,第一遍DP处理出一个点到它的子树内的最优值。第二遍的时候,对于每一个点(i)记录一下一个权值(dp_i - pre_i)((pre_i)为当前点到根的前缀和),在更新(i)号点的时候,我们在它的所有祖先中找一个这个权值最小的点(j),然后用(dp_j - pre_j + pre_i)来更新当前点就可以了
Code
#include <bits/stdc++.h>
using namespace std;
#define squ(x) ((LL)(x) * (x))
#define debug(...) fprintf(stderr, __VA_ARGS__)
typedef long long LL;
typedef pair<int, int> pii;
inline int read() {
int sum = 0, fg = 1; char c = getchar();
for (; !isdigit(c); c = getchar()) if (c == '-') fg = -1;
for (; isdigit(c); c = getchar()) sum = (sum << 3) + (sum << 1) + (c ^ 0x30);
return fg * sum;
}
const int maxn = 1e5 + 10;
const int inf = 0x3f3f3f3f;
int n, q;
namespace Tree {
vector<int> g[maxn];
int d[maxn], fa[maxn][17], Max[maxn][17];
int w[maxn], pre[maxn];
pii dp[maxn];
void input() {
for (int i = 2; i <= n; i++) g[read()].push_back(i);
for (int i = 1; i <= n; i++) w[i] = read();
}
void dfs1(int now, int f) {
dp[now] = (pii){inf, inf}, pre[now] = pre[f] + w[now];
d[now] = d[f] + 1, fa[now][0] = f, Max[now][0] = max(w[now], w[f]);
for (int i = 1; i <= 16; i++) {
fa[now][i] = fa[fa[now][i - 1]][i - 1];
Max[now][i] = max(Max[now][i - 1], Max[fa[now][i - 1]][i - 1]);
}
for (int son : g[now]) {
dfs1(son, now);
pii tmp = (pii){dp[son].first + w[now], min(dp[son].second, -w[now])};
dp[now] = min(dp[now], tmp);
}
if (dp[now] == (pii){inf, inf}) dp[now] = (pii){w[now], -w[now]};
}
void dfs2(int now, pii pp) {
pii tmp = (pii){pp.first + pre[now], min(pp.second, -w[now])};
dp[now] = min(dp[now], tmp);
tmp = (pii){dp[now].first - pre[now], dp[now].second};
pp = min(pp, tmp);
for (int son : g[now]) dfs2(son, pp);
}
int lca(int x, int y) {
if (d[x] < d[y]) swap(x, y);
for (int i = 16; ~i; i--)
if (d[fa[x][i]] >= d[y]) x = fa[x][i];
if (x == y) return x;
for (int i = 16; ~i; i--)
if (fa[x][i] != fa[y][i]) x = fa[x][i], y = fa[y][i];
return fa[x][0];
}
int sum(int x, int y) {
int f = lca(x, y), ff = fa[f][0];
return pre[x] + pre[y] - pre[f] - pre[ff];
}
int get_max(int x, int y) {
int MAX = max(w[x], w[y]), ff = lca(x, y);
for (int i = 16; ~i; i--)
if (d[fa[x][i]] >= d[ff]) MAX = max(MAX, Max[x][i]), x = fa[x][i];
for (int i = 16; ~i; i--)
if (d[fa[y][i]] >= d[ff]) MAX = max(MAX, Max[y][i]), y = fa[y][i];
return MAX;
}
void query(int x, int y) {
pii ans = (pii){sum(x, y), -get_max(x, y)};
pii t1 = (pii){dp[x].first + pre[y], min(-get_max(y, 1), dp[x].second)};
pii t2 = (pii){dp[y].first + pre[x], min(-get_max(x, 1), dp[y].second)};
pii t3 = (pii){dp[x].first + dp[y].first + w[1], min(-w[1], min(dp[x].second, dp[y].second))};
ans = min(ans, t1), ans = min(ans, t2), ans = min(ans, t3);
printf("%d %d
", ans.first, -ans.second);
}
}
int main() {
freopen("plutotree.in", "r", stdin);
freopen("plutotree.out", "w", stdout);
n = read(), q = read();
Tree::input();
Tree::dfs1(1, 0), Tree::dfs2(1, (pii){inf, inf});
while (q--) {
int x = read(), y = read();
Tree::query(x, y);
}
return 0;
}
Summary
我是真的菜,一开始一直以为这题不能DP....
这是我第一次接触这种多次更新答案的树形DP题,又学到了一个新的技巧