算法知识点: DFS,树的深度优先遍历
复杂度:
解题思路:
距离为2的点对有两种:八字形和1字形。
对于八字形:直接将当前节点的所有子节点两两配对的结果统计出来即可。这一步线性扫描一遍即可,不需要 枚举。
我们以求总和为例,求最大值类似。从前往后枚举子节点时维护变量 ,表示前面所有子节点的权值和,则每次将
与当前节点的权值的乘积累加起来,就是当前节点的所有子节点两两配对的总权值和。
对于1字形,我们在DFS过程中对每个点维护两个值:表示节点
的所有子节点权值的最大值,
表示节点
的所有子节点权值之和。那么以
为最高点的1字形点对权值之和就是
的权值乘以
的所有子节点的
之和。求最大值类似。
由于题目中要求的是有序点对的和,因此权值和需要乘2。
C++ 代码:
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std; const int N = 200010,
M = N * 2,
mod = 10007;
int n;
int h[N], e[M], ne[M], idx;
int w[N];
int ans_max, ans_sum;
int f[N], g[N];
void add(int a, int b)
{
e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}
void dfs(int u, int father)
{
int sum = 0, maxv = 0;
for (int i = h[u]; ~i; i = ne[i])
{
int j = e[i];
if (j != father)
{
dfs(j, u);
ans_max = max(ans_max, w[u] *f[j]);
ans_max = max(ans_max, maxv *w[j]);
maxv = max(maxv, w[j]);
ans_sum = (ans_sum + w[u] *g[j]) % mod;
ans_sum = (ans_sum + sum *w[j]) % mod;
sum = (sum + w[j]) % mod;
f[u] = max(f[u], w[j]);
g[u] = (g[u] + w[j]) % mod;
}
}
}
int main()
{
scanf("%d", &n);
memset(h, -1, sizeof h);
for (int i = 0; i < n - 1; i++)
{
int a, b;
scanf("%d%d", &a, &b);
add(a, b), add(b, a);
}
for (int i = 1; i <= n; i++) scanf("%d", &w[i]);
dfs(1, -1);
printf("%d %d\n", ans_max, ans_sum * 2 % mod);
return 0;
} 
京公网安备 11010502036488号