链接:http://codeforces.com/problemset/problem/915/F
题意:树上每一个点有一个点权,求树上所有的路径的(最大点权值-最小点权值)之和
题解:可以考虑先求出所有路径中最大点权值之和。显然,每一个点都能对一些路径产生贡献,我们可以先按点权从小到大排序,然后每次取一个点,那么所取的点就是在当前已取出的点中最大的。用并查集维护之前取出的在不同子树中的点以及数量,并两两相乘,更新当前点的贡献。这一步有点像之前的一道单调栈的题目,求区间的数目,这些区间中的最大值是某个点,大概就是用该点左边能扩展的长度乘上右边扩展的长度。这也是这一类树上统计的方法。
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
using namespace std;
int n;
vector<int> G[1000010];
struct node{
long long w;
int id;
}p[1000010];
bool cmp(node a, node b){
return a.w<b.w;
}
long long res;
int fa[1000010];
int vis[1000010];
int num[1000010]; //以x为根的子树的节点个数
int Find(int x){
return fa[x] == x ? x:(fa[x]=Find(fa[x]));
}
void solve(){
for(int i = 1; i<=n; i++){
fa[i] = i;
num[i] = 1;
vis[i] = 0;
}
sort(p+1, p+1+n, cmp);
for(int i = 1; i<=n; i++){
for(int j = 0; j<G[p[i].id].size(); j++){
int v = G[p[i].id][j];
int temp = Find(v);
if(!vis[temp]) continue; //注意是要找已经访问过的
res+=(long long)p[i].w*num[temp]*num[p[i].id];
fa[temp] = p[i].id;
num[p[i].id]+=num[temp];
}
vis[p[i].id] = 1;
p[i].w = -p[i].w;
}
//cout<<res<<endl;
}
int main(){
cin>>n;
for(int i = 1; i<=n; i++){
p[i].id = i;
scanf("%I64d", &p[i].w);
}
for(int i = 1; i<n; i++){
int u, v;
scanf("%d%d", &u, &v);
G[u].push_back(v);
G[v].push_back(u);
}
res = 0;
solve();
//cout<<res<<endl;
solve();
printf("%I64d\n", res);
return 0;
}