题意:
给定你
n
n
n个点,
m
m
m条边,每个点都有一个点权值
v
i
v_i
vi,然后我们这样定义
f
(
p
,
q
)
f(p,q)
f(p,q),假设从点
p
p
p到
q
q
q的有若干条简单路径,每条路径中都有一个最小值,
f
(
p
,
q
)
f(p,q)
f(p,q)等于这个若干个最小值中的最大值。然后现在让你求出所有的点对(u,v)的
f
(
u
,
v
)
f(u,v)
f(u,v)的和,并除点对数,求平均值。
思路:
我们的目标就是要求的这个点对的和为多少。
很明显数据范围就告诉我们,我们应该考虑算每个点权的贡献,这里需要第一个转化把点权值转化成边权值,对于点对
(
u
,
v
)
(u,v)
(u,v),令边权等于
m
i
n
(
v
u
,
v
v
)
min(v_u,v_v)
min(vu,vv)即可。
然后考虑怎么使得某条边成为最小值的最大边去计算贡献,两个点之间可能被多条边给联通,我们按边权从大到小的去维护这个图的连通性,就可以保证当前枚举到的边,如果边上的两点没有联通,那么此时这条边一定连接他们所属的两个不同的联通块的最大的那条边,其他的边只会更小,保证了最大这个要求。并且我们从大到小枚举,那么此前加入两个连通块的边一定是比当前这条边的权值要大,所以最小边这个条件又符合。
至此,我们只需维护两个联通的块的大小
s
i
z
e
1
,
s
i
z
e
2
size_1,size_2
size1,size2,每条合法的边的贡献就是
w
∗
s
i
z
e
1
∗
s
i
z
e
2
∗
2
w * size_1 * size_2 * 2
w∗size1∗size2∗2。
有点套路题的感觉…
#include <bits/stdc++.h>
using namespace std;
#define pb emplace_back
#define MP make_pair
#define pii pair<int,int>
#define pll pair<ll,ll>
#define lson rt<<1
#define rson rt<<1|1
#define CLOSE std::ios::sync_with_stdio(false)
#define sz(x) (int)(x).size()
typedef long long ll;
typedef double db;
const int INF = 0x3f3f3f3f;
const db eps = 1e-6;
const int N = 1e5 + 10;
int n,m,a[N],tot,fa[N],siz[N];
//任意两点间的简单路径上的点权值的最小值最大 用最大生成树维护这个过程
struct Edge {
int x,y;
ll w;
bool operator < (const Edge& a) const {
return w > a.w;
}
}edge[N<<1];
void addedge(int u,int v,int w) {
edge[++tot].x = u,edge[tot].y = v,edge[tot].w = w;
// edge[++tot].from = v,edge[tot].to = u,edge[tot].w = w;
}
ll ans;
int Find(int x) {
if(fa[x] == x) return x;
else return fa[x] = Find(fa[x]);
}
void merge(int x,int y,ll w) {
fa[y] = x;
// siz[y] += siz[x];
ans += w * siz[y] * siz[x] * 2;
siz[x] += siz[y];
}
int main() {
scanf("%d%d",&n,&m);
for(int i = 1;i <= n;i ++) fa[i] = i;
for(int i = 1;i <= n;i ++) siz[i] = 1;
for(int i = 1;i <= n;i ++) scanf("%d",&a[i]);
int u,v;
for(int _ = 1;_ <= m;_ ++) {
scanf("%d%d",&u,&v);
addedge(u,v,min(a[u],a[v]));
}
sort(edge + 1,edge + 1 + tot);
for(int i = 1;i <= tot;i ++) {
int u = edge[i].x,v = edge[i].y;
int fu = Find(u),fv = Find(v);
if(fu != fv) {
merge(fu,fv,edge[i].w);
}
}
db res = 1.0 * ans / (1ll * n * (n - 1));
printf("%f\n",res);
return 0;
}