cogs2478 简单的最近公共祖先
【题目描述】
给定一棵有n个节点的有根树,根节点为1,每个节点有一个权值wi,求
即求所有无序节点对的LCA的权值之和。
树的节点编号为1~n,LCA表示两节点的最近公共祖先,即在它们的所有公共祖先中离根节点最远的节点。
【输入格式】
第一行一个整数n,表示节点数。
第二行n个正整数,表示每个点的权值。
以下n-1行每行两个整数x,y,表示树上有一条边连接节点x和节点y。
【输出格式】
一个整数,表示答案。
【样例输入】
3 1 2 3 1 2 1 3【样例输出】
9【数据范围与约定】
对于30%的数据,n<=1000。
对于60%的数据,n<=100000。
对于100%的数据,1<=n<=1000000,0<wi<=1000000。
呃。。。很明显不是枚举所有点对。。。
容易发现,对于一个节点来说,它作为lca的时候当且仅当任意两个子节点属于该节点的两个不相同的子树。
然后就乘法原理+前缀和优化就行了。
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <vector>
using namespace std;
#define FILENAME "easy_LCA"
typedef long long ll;
const int N = 2000010;
int n, head[N], rest[N], to[N], w[N], size[N], tot;
void add(int u, int v) {
tot ++;
to[tot] = v;
rest[tot] = head[u];
head[u] = tot;
}
ll ret;
void dfs(int u) {
size[u] = 1;
for(int i = head[u] ; i ; i = rest[i]) {
if(size[to[i]] == 0) {
dfs(to[i]);
ret += (ll)size[u] * (ll)size[to[i]] * (ll)w[u];
size[u] += size[to[i]];
}
}
ret += w[u];
}
int main() {
freopen(FILENAME ".in", "r", stdin);
freopen(FILENAME ".out", "w", stdout);
scanf("%d", &n);
for(int i = 1 ; i <= n ; i ++) {
scanf("%d", &w[i]);
}
for(int i = 1, u, v ; i < n ; i ++) {
scanf("%d%d", &u, &v);
add(u, v);
add(v, u);
}
dfs(1);
printf("%lld\n", ret);
}


浙公网安备 33010602011771号