链接: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; }