神奇的Markdown~~
题意
给定n个点,m条边,每条边有权值,求图的最小生成树,保证有解,要求在边权值和最小的情况。在求出最小生成树后,求出所有路径(最短路)的期望
思路
最小生成树可以直接Prim或者Kruskal求,对于路径的期望值,我们发现一共有 n∗(n−1)2 种情况,而经过一条边的方案数,等于这条边两侧的点的数量的乘积。
那么如何求一条边两边的点个数呢? DFS/BFS建立有根树就好,每个点都会有一个深度值
x
。一条边连接的两个点中,一定有一个更靠近根节点,所以权值较小的那个点代表的数值
一共有 n−1 条边被选中,答案为 ∑n−1i=0ti∗(n−ti)%Mod
#include <bits/stdc++.h>
#define mem(a,b) memset(a,b,sizeof(a))
#define rep(i,a,b) for(int i=a;i<b;i++)
#define debug(a) printf("a =: %d\n",a);
const int INF=0x3f3f3f3f;
const int maxn=1e5+50;
const int Mod=1e9+7;
const double PI=acos(-1);
typedef long long ll;
using namespace std;
struct Edge{
int from,to;
ll cost;
Edge(){}
Edge(int a,int b,ll c){
from=a; to=b; cost=c;
}
bool operator<(const Edge&rhs)const{
return cost<rhs.cost;
}
};
Edge edges[maxn*10];
int fa[maxn];
int n,m;
int find(int x){
return fa[x]==x? x:fa[x]=find(fa[x]);
}
vector<int> G[maxn];
int du[maxn];
Edge cs[maxn*10];
int cnt;
//bool vis[maxn];
int dfs(int cur,int parent){
int sz=G[cur].size();
int sum=1;
for(int i=0;i<sz;i++){
int v=G[cur][i];
if (v!=parent /*&& !vis[v]*/){
sum+=dfs(v,cur);
}
}
du[cur]=sum;
return sum;
}
void init(){
for(int i=1;i<=n;i++) {
G[i].clear();
fa[i]=i;
}
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("in.txt","r",stdin);
#endif
int T; scanf("%d",&T);
while(T--){
int u,v,c;
scanf("%d %d",&n,&m);
init();
for (int i=0;i<m;i++){
scanf("%d %d %d",&u,&v,&c);
edges[i].from=u;
edges[i].to=v;
edges[i].cost=c;
}
sort(edges,edges+m);
ll sum=0;
cnt=0;
mem(du,0);
// mem(vis,0);
for (int i=0;i<m;i++){
int u=edges[i].from;
int v=edges[i].to;
int fu=find(u);
int fv=find(v);
if (fu!=fv) {
fa[fu]=fv;
sum=sum+edges[i].cost;
G[u].push_back(v);
G[v].push_back(u);
cs[cnt++]=edges[i];
}
}
dfs(1,-1);
// for(int i=1;i<=n;i++) cout<<du[i]<<'\n';
double expectation=0;
for(int i=0;i<cnt;i++){
int u=cs[i].from;
int v=cs[i].to;
int dMin=min(du[u],du[v]);
int dMax=n-dMin;
expectation+=(ll)dMin*dMax*cs[i].cost;
}
expectation=expectation/n/(n-1)*2;
printf("%lld %.2lf\n",sum,expectation);
}
return 0;
}