题目链接:
题目大意:
给定一棵树,有n个点,每个点(假设点为v)给定两个值分别是lv和rv,再给定n-1条边,对于每条边来说它的贡献值是|ai - aj|,i 和 j 是边的两个点,ai 是介于 li 和 ri 之间的任意一个值,aj 同理。 求这个树最大的贡献值是多少。
解题思路:
第一时间的想法就是每个点必须选取极端的值,因为对于任意边来说,两点差的绝对值最大肯定是极端值的情况。
以Test case 3的4,6讨论一下:
- 很明显4,6的最大贡献值要选取2和17
- 如果不这么选而选取中间值,那就只有一个理由:2和3的那条边影响到了4
- 但是下面那条边如果想要选取出最大贡献,也要取极端值
- 而如果6号点选取了中间值,那很明显对于4和6的贡献都不是最大的
- 什么,2和4也不选取极端值,那你可以选择暴力枚举出来,6、2、3他们的贡献值一定没刚才大

当然,上面只是为了说明选取极端值才是最好的,并不是最重要的东西。
只有选取极端值我们才可以用dp来做。(不然枚举的第二维太多了,1e9存都存不下)
还以Test case 3的6、4、2、3举例,他们的最大贡献无非就是左右枚举一遍,即(3,2,12,12)、(3,12,20,19)。。。。。
那么爆搜一遍答案其实就出来了,并且是正确的,如下:
TLE代码:
#include <bits/stdc++.h>using namespace std;const int N = 1e5 + 50;typedef long long LL;int e[N], ne[N], h[N], idx;void add(int a, int b){e[idx] = b, ne[idx] = h[a], h[a] = idx ++ ;}int l[N], r[N];int t, n;int f[N];int dfs(int u, int cs, int f) {int dist = 0;for (int i = h[u]; ~i; i = ne[i]) {int j = e[i];if (j == f) continue; // 防止死循环// 就是这里,选取相邻结点的两个极端值即可dist += max(dfs(j, l[j], u) + abs(cs - l[j]), dfs(j, r[j], u) + abs(cs - r[j]));}return dist;}int main(){cin >> t;while (t -- ) {memset(h, -1, sizeof h);scanf("%d", &n);for (int i = 1; i <= n; i ++ ) scanf("%d%d", &l[i], &r[i]);for (int i = 0; i < n - 1; i ++ ) {int a, b;scanf("%d%d", &a, &b);if (a > b) swap(a, b);add(a, b);add(b, a);}printf("%d\n", max(dfs(1, l[1], -1), dfs(1, r[1], -1)));}return 0;}
但是很容易发现,这个复杂度是指数级别的,所以我很快啊,啪的一下就TLE了。。 仔细看其实这个代码是从上往下找的,即要往左找一遍,往右找一遍,这才导致复杂度高。 如果先处理出来子节点的左右max值,当前节点直接用,就变成了线性 可以这么做的原因是并不像传统的二叉树有两个儿子,这题只有一个儿子,但是有两个值而已
AC代码:
#include <bits/stdc++.h>using namespace std;typedef long long LL;const int N = 1e6 + 20;int n;int e[N], ne[N], h[N], idx;LL l[N], r[N];void add(int a, int b) {e[idx] = b, ne[idx] = h[a], h[a] = idx ++ ;}int t;LL dp[N][2];void dfs(int u, int f) {dp[u][0] = dp[u][1] = 0;for (int i = h[u]; ~i ; i = ne[i]) {int j = e[i];if (j == f) continue;// 这里让他先递归下去,记录下以j为根的最优解dfs(j, u);// 这里方程没变,直接用就可以了// 此时递归两次变成了递归一次,但是一次就记录了最优解dp[u][0] += max(dp[j][0] + abs(l[u] - l[j]), dp[j][1] + abs(l[u] - r[j]));dp[u][1] += max(dp[j][0] + abs(r[u] - l[j]), dp[j][1] + abs(r[u] - r[j]));}}int main(){cin >> t;while (t -- ) {memset(h, -1, sizeof h);scanf("%d", &n);for (int i = 1; i <= n; i ++ ) scanf("%d%d", &l[i], &r[i]);for (int i = 0; i < n - 1; i ++ ) {int a, b;scanf("%d%d", &a, &b);add(a, b);add(b, a);}dfs(1, -1);printf("%lld\n", max(dp[1][0], dp[1][1]));}return 0;}
