「Imbalance Value of a Tree」Solution

简述题意

给定一颗 n n n 个节点的树,每个点有一个点权 a i a_i ai
定义 I ( x , y ) I(x,y) I(x,y) 表示 x x x y y y 的路径上最大点权与最小点权的差值。
求出 ∑ i = 1 n ∑ j = i n I ( i , j ) \sum_{i=1}^{n}\sum_{j=i}^{n}I(i,j) i=1nj=inI(i,j)

  • 1 ≤ n , a i ≤ 1 0 6 1 \le n,a_i \le 10^6 1n,ai106

思路

很显然最大值和最小值可以拆开维护,即所有简单的路径的点权最大值减去点权最小值。拆完贡献后,枚举每一个点,求出它对多少条简单路径有贡献,到这一步都是显然的。

那么此题便转化为,求出经过 i i i 的简单路径,有多少路径上的点权最小/大值等于 a i a_i ai,对于此类钦定最小/大值 + 集合计数的问题,且值域较小,套路地用并查集维护。

不妨以最大值为例,我们从小到大枚举点权 x x x,每次把 a i = x a_i=x ai=x i i i 与其相连且满足 a v < a u a_v < a_u av<au v v v 合并,那么,我们便可以保证,合并之前在 i i i 所属的集合任选一个点,在 v v v 所属的集合任选一个点,这两点间的简单路径的最大点权就是 x x x,且合并后的集合再和别的集合合并,也是满足这个条件的。因此,一次合并对答案的贡献即为 s i z i × s i z v × x siz_i \times siz_v \times x sizi×sizv×x,其中 s i z i siz_i sizi 表示 i i i 所属集合的点数。

最小值同理,只不过对答案的贡献是负的。

代码

此题只要想到并查集的 Trick \text{Trick} Trick 就很显然了,思维和代码难度都很小,然而如果想偏了基本上就不可做了。(可见一个正确的思想多么重要

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int MAXN = 1e6 + 5;
int n , a[MAXN] , ans , now;
vector<int> E[MAXN] , id[MAXN];
namespace DSU{
	int fa[MAXN] , siz[MAXN];
	void MakeSet(int n) {for (int i = 1 ; i <= n ; i ++) fa[i] = i , siz[i] = 1;}
	int FindSet(int x) {return (fa[x] == x ? x : fa[x] = FindSet(fa[x]));}
	void UnionSet(int x , int y) {
		x = FindSet(x) , y = FindSet(y);
		if (x == y) return;
		ans += siz[x] * siz[y] * now;
		fa[x] = y , siz[y] += siz[x];
	}
}
using namespace DSU;
signed main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr) , cout.tie(nullptr);
	cin >> n;
	MakeSet(n);
	for (int i = 1 ; i <= n ; i ++) cin >> a[i] , id[a[i]].push_back(i);
	for (int i = 1 , u , v ; i < n ; i ++) {
		cin >> u >> v;
		E[u].push_back(v) , E[v].push_back(u);
	}
	for (int i = 1 ; i <= 1000000 ; i ++) {
		now = i;
		for (int u : id[i]) {
			for (int v : E[u]) {
				if (a[v] > a[u]) continue;
				UnionSet(u , v);
			}
		}
	}
	MakeSet(n);
	for (int i = 1000000 ; i >= 1 ; i --) {
		now = -i;
		for (int u : id[i]) {
			for (int v : E[u]) {
				if (a[v] < a[u]) continue;
				UnionSet(u , v);
			}
		}
	}
	cout << ans;
	return 0;
}
  • 35
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值