洛谷 P1351 联合权值
洛谷 P1351 联合权值
思路
一道洛谷的绿题,虽然不是很难,但我还是做了很长时间
\(n\)个点,\(n-1\)条边,我们很容易就能想到这是一棵树,看数据范围,如果暴力枚举的话,只能拿部分分,这时候我们就回到题目中看,发现,只有距离为\(2\)才会产生价值的话,如果枚举一个节点,只有 它和它的孙子之间 或者 它的两个儿子之间 才会产生价值,所以我们就可以枚举这个节点,进行\(DFS\),期间进行更新就好了,具体的实现可以看代码
代码
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
inline int read() {
char c = getchar();
int x = 0, f = 1;
for( ; !isdigit(c); c = getchar()) if(c == '-') f = -1;
for( ; isdigit(c); c = getchar()) x = (x << 3) + (x << 1) + (c ^ 48);
return x * f;
}
const int N = 1e6 + 11;
const int M = 2e5 + 7;
const int mod = 10007;
//邻接表存图
struct node {
int to, nxt;
} e[N];
int head[M], cnt = 0;
inline void add(int from, int to) {
e[++cnt].to = to;
e[cnt].nxt = head[from];
head[from] = cnt;
}
int n, w[M], x, y, tot[M], ma[M], cma[M], ans, maxn;
//tot[x]数组记录x所有儿子的价值,ma[x]记录x所有儿子中的最大值,cma[x]记录x所有儿子中的次大值
//DFS!!
void dfs(int u, int fa) {
//与孙子之间产生的价值
for(int i = head[u]; i; i = e[i].nxt) { //枚举儿子节点
int v = e[i].to;
if(v == fa) continue;
dfs(v, u);
tot[u] = (tot[u] + w[v]) % mod; //tot[u]加上它儿子v的价值
if(w[v] > ma[u]) ma[u] = w[v]; //找它所有儿子的最大值
else cma[u] = max(cma[u], w[v]);
maxn = max(maxn, w[u] * ma[v]);//答案中的最大值
ans = (ans + 2 * w[u] * tot[v] % mod) % mod;//与孙子之间产生的价值(他到他孙子之间的距离一定是2),因为是双向的,所以要乘2
}
//儿子之间产生的价值
for(int i = head[u]; i; i = e[i].nxt) {
int v = e[i].to;
if(v == fa) continue;
if(w[v] == ma[u]) maxn = max(maxn, w[v] * cma[u]); //如果是最大值就与次大值相乘
else maxn = max(maxn, w[v] * ma[u]); //否则与最大值相乘
ans = (ans + w[v] * (tot[u] - w[v]) % mod) % mod; //其中一个儿子与父节点所有儿子之间产生的价值就是w[v] * (tot[u] - w[v])
}
}
int main() {
n = read();
for(int i = 1; i < n; i++) {
x = read(), y = read();
add(x, y);
add(y, x);
}
for(int i = 1; i <= n; i++) w[i] = read();//cout << w[i] << '\n';
dfs(1, 0);
while(ans < 0) ans += mod; //答案可能是负的,只要是负的就一直加模数
cout << maxn << " " << ans << '\n';
return 0;
}
转载不必联系作者,但请声明出处