最小生成树+树上的计数:
因为任意两条边的权值不同,所以最小生成树唯一。算出树上任意两点间距离求和(poj上有道题就是求树上任意两点间距离求和 http://poj.org/problem?id=4045 ),再除以n*(n-1)就是期望。
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<vector>
#include<cmath>
#include<string>
#include<algorithm>
#include<set>
#include<map>
#include<cstring>
#include<queue>
#include<stack>
#include<list>
using namespace std;
typedef long long ll;
const int maxn=100000+10;
const ll inf=1e9;
const ll mod=1e9+7;
int fa[maxn];
int getfa(int x){
return fa[x]==0?x:(fa[x]=getfa(fa[x])) ;
}
struct node{
int x,y;ll z;
bool operator<(node tt)const{
return z<tt.z;
}
}e[1000000+10];
int siz;
struct haha{
int to;ll val;
};
vector<haha> g[maxn];
void ini(){
for(int i=0;i<maxn;i++){
g[i].clear();
}
}
double sum[maxn];ll L[maxn];
int vis[maxn];
double ans;
ll dfs(int x){
L[x]=1;
for(int i=0;i<g[x].size();i++){
if( vis[ g[x][i].to ]==0 ){
vis[ g[x][i].to ]=1;
sum[x]=sum[x]+dfs( g[x][i].to )+g[x][i].val*L[g[x][i].to ];
L[x]+=L[g[x][i].to ];
}
}
return sum[x];
}
ll sol(int x){
for(int i=0;i<g[x].size();i++){
if( vis[ g[x][i].to ]==0 ){
vis[g[x][i].to ]=1;
sum[ g[x][i].to ]=sum[x]+L[x]*g[x][i].val-2*g[x][i].val*L[ g[x][i].to ];
L[ g[x][i].to ]=L[x];
ans+=sum[g[x][i].to];
sol( g[x][i].to );
}
}
}
int main()
{
int t;
scanf("%d",&t);
while(t--){
ll n,m;
scanf("%I64d%I64d",&n,&m);
siz=0;
ans=0;
for(int i=0;i<m;i++){
int x,y;ll z;
scanf("%d%d%I64d",&x,&y,&z);
e[siz++]=node{x,y,z};
}
sort(e,e+siz);
ini();
memset(fa,0,sizeof(fa));
ll res=0;
for(int i=0;i<siz;i++){
int fax=getfa(e[i].x);
int fay=getfa(e[i].y);
if(fax!=fay){
fa[fax]=fay;
res+=e[i].z;
g[e[i].x].push_back(haha{e[i].y,e[i].z});
g[e[i].y].push_back(haha{e[i].x,e[i].z});
}
}
memset(sum,0,sizeof(sum));
memset(L,0,sizeof(L));
memset(vis,0,sizeof(vis));
vis[1]=1;
dfs(1);
memset(vis,0,sizeof(vis));
vis[1]=1;
sol(1);
ans+=sum[1];
if(n<=1){printf("0 0.00\n");continue;}
printf("%I64d %.2f\n",res,ans*1.0/(n*(n-1)));
}
return 0;
}