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);
}

  

posted @ 2017-08-22 17:17  KingSann  阅读(108)  评论(0)    收藏  举报