众所周知,小 naive 有一张 nn 个点,mm 条边的带权无向图。

ii 个点的颜色为 cic_id(s,t)d(s, t)表示从点 s 到点 t 的权值最小的路径的权值,一条路径的权值定义为路径上权值最大的边的权值。

求所有满足 u<v,cucvLu < v, |c_u - c_v | \ge L 的点对 (u,v)(u, v)d(u,v)d(u, v) 之和。

n2×105,m5×105n\le 2 \times 10^5, m\le 5 \times 10^5

Solution

可以发现,产生贡献的答案一定是最小生成树上的边

对于一条新加进最小生成树的边(x,y,z)(x,y,z)而言,在xx所在连通块中的所有点都会对yy所在连通块中的所有满足cucvL|c_u -c_v|\ge L的点产生贡献

那么对每个连通块维护一颗平衡树就可以了,并且需要启发式合并

由于平衡树需要支持的操作很少,所以只需要pb_dspb\_ds

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
#include<bits/stdc++.h>
#include<bits/extc++.h>
using namespace std;
using namespace __gnu_pbds;
inline void read(int &x){
char c = getchar();
int p = 1;
x = 0;
while(!isdigit(c)){
if(c == '-')p = -1;
c = getchar();
}
while(isdigit(c)){
x = (x << 1) + (x << 3) + (c ^ '0');
c = getchar();
}
x *= p;
}
const int maxn = 200050, maxm = 500050;
struct edge{
int u, v, w;
}e[maxm];
#define mp make_pair
tree<pair<int, int>, null_type, less<pair<int, int> >, rb_tree_tag, tree_order_statistics_node_update>se[maxn];
tree<pair<int, int>, null_type, less<pair<int, int> >, rb_tree_tag, tree_order_statistics_node_update>::iterator it;
int c[maxn], n, m, l, f[maxn], cnt;
long long ans;
inline void init(){
for(register int i = 0; i <= n + 1; ++i){
f[i] = i;
}
}
inline int getf(int u){
return f[u] == u ? u : f[u] = getf(f[u]);
}
inline void merg(int u, int v){
f[u] = v;
}
inline bool cmp(edge a, edge b){
return a.w < b.w;
}
int main(){
freopen("graph.in", "r", stdin);
freopen("graph.out", "w", stdout);
read(n), read(m), read(l);
init();
for(register int i = 1; i <= n; ++i){
read(c[i]);
se[i].insert(mp(c[i], ++cnt));
}
for(register int i = 1; i <= m; ++i){
read(e[i].u), read(e[i].v), read(e[i].w);
}
sort(e + 1, e + 1 + m, cmp);
int fu, fv, szu, szv, dis1, dis2;
for(register int i = 1; i <= m; ++i){
fu = getf(e[i].u), fv = getf(e[i].v);
if(fu != fv){
szu = se[fu].size(); szv = se[fv].size();
if(szu > szv)swap(fu, fv), swap(szu, szv);
for(it = se[fu].begin(); it != se[fu].end(); ++it){
int val = (*it).first + l;
dis1 = se[fv].order_of_key(mp(val - 1, ++cnt));
dis1 = szv - dis1;
if(dis1 > 0)ans += 1ll * e[i].w * dis1;
val = (*it).first - l;
dis2 = se[fv].order_of_key(mp(val, ++cnt));
if(dis1 + dis2 > szv && l == 0){
dis2 = szv - dis1;
}
if(dis2 > 0)ans += 1ll * e[i].w * dis2;
}
for(it = se[fu].begin(); it != se[fu].end(); ++it){
se[fv].insert(mp((*it).first, ++cnt));
}
se[fu].clear();
merg(fu, fv);
}
}
printf("%lld\n", ans);
return 0;
}