题目描述
Hja有一棵N个点的树,树上每个点有点权,每条边有颜色。
一条路径的权值是这条路径上所有点的点权和,一条合法的路径需要满足该路径上任意相邻的两条边颜色都不相同。
问这棵树上所有合法路径的权值和是多少。
输入
第一行一个数N。
接下来一行N个数代表每个点的权值。
接下来N−1行每行三个整数s,e,c,代表s到e之间有一条颜色为c的边。
输出
一行一个整数代表答案。
样例输入
6
6 2 3 7 1 4
1 2 1
1 3 2
1 4 3
2 5 1
2 6 2
样例输出
134
提示
对于30%的数据,1≤N≤1000。
对于另外20%的数据,数据随机。
对于另外20%的数据,是一条链。
对于100%的数据,(1≤N≤3×10^5,1≤c≤10^9)。
树形dp,(val[i])表示以i为路径一个端点,其子树中一点为另一点时的权值和,cnt[i]表示以i为路径一个端点,其子树中一点为另一点时的方案数。
答案的计算:
- i为路径端点
- i为一个端点,其子树中一点为另一个端点
- i出现在其它点的子树中,作为端点
- i为路径上的点
- 两个端点在其子树中
- 一个端点在子树中,另一个在i的祖先中
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int inf = 0x3f3f3f3f;
const int mod = 1e9 + 7;
const int maxn = 2e6 + 100;
struct Edge {
int to, next, col;
} e[maxn];
int head[maxn];
int tot;
ll a[maxn], ans, val[maxn], cnt[maxn];
void addedge(int u, int v, int col) {
tot++;
e[tot].to = v, e[tot].col = col, e[tot].next = head[u], head[u] = tot;
}
void dfs(int now, int pre, int pcol) {
for (int i = head[now]; i; i = e[i].next) {
if (e[i].to == pre) continue;
dfs(e[i].to, now, e[i].col);
if (e[i].col != pcol) {
cnt[now] += cnt[e[i].to];
val[now] += cnt[e[i].to] * a[now] + val[e[i].to];
}
ans += cnt[e[i].to] * a[now] + val[e[i].to];//以now为一端,子树中一点为另一端时的贡献
}
cnt[now] += 1, val[now] += a[now];
for (int i = head[now]; i; i = e[i].next) {
if (e[i].to == pre) continue;
for (int j = i; j; j = e[j].next) {
if (e[j].to == pre)continue;
if (e[i].col != e[j].col) {
ans += cnt[e[i].to] * val[e[j].to] + cnt[e[j].to] * val[e[i].to] + a[now] * cnt[e[i].to] * cnt[e[j].to];//两个端点在now的子树中
}
}
}
}
int main() {
#ifndef ONLINE_JUDGE
freopen("in.txt", "r", stdin);
#endif
int n;
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
scanf("%lld", &a[i]);
}
int u, v, w;
for (int i = 1; i < n; i++) {
scanf("%d %d %d", &u, &v, &w);
addedge(u, v, w);
addedge(v, u, w);
}
dfs(1, 0, 0);
printf("%lld
", ans);
return 0;
}