原题链接:http://acm.hdu.edu.cn/showproblem.php?pid=5723
感悟:
本来已经学过也练过一些图论基础的题目,但是因为学长没在VJ上挂最小生成树的题直接挂了最短路和最大流的题,所以没做过最小生成树的题,但是在学了离散数学还是了解最小生成树的一些概念。拿到题,仔细思考,一开始队友说是哈密顿图,于是我翻了翻kuangbin模板,最后感觉是最小生成树。花10分钟敲了一下Kruskal算法,第一个输出值直接就可以了。其中百度了一下“树中任意两点之间距离的数学期望”,没找到,但找到了“树中任意两点之间距离的平均值”,看了一会感觉没啥用 =- =,所以最终坑在了上面
分析:
看了官方题解,仔细回味,才发现他么期望就是数学期望,两个是一样的。。。我当时真是日狗了。dfs一发,记录一条边的一点一侧的点数num[i]
,这条边的另一点的相反侧的点数就是n-num[i]
,所以这个边被经过的次数就是num[i] ×(n-num[i])
,再乘上这条边的长度len,就是对总的距离和的贡献。最后除以(n×(n-1)/2)
就是最后的期望。特别要注意int 和long long 的转换,这里我因为吗,long long s=(long long)n*(n-1)/2;
没注意,WA了很久,有时还莫名的TLE。最后改了这里才AC。
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<set>
#include<map>
#include<algorithm>
#include<string>
#include<queue>
#include<cmath>
#include<stack>
using namespace std;
const int maxn=100010;//最大点数
const int maxm=1000010;//最大边数
int F[maxn];//并查集,对应父节点
struct Edge{
int u,v,w;
}edge[maxm];
struct Dis{
int v,w;
};
vector<Dis> D[maxn];//最小生成树的邻接表
double dp[maxn];
int sum[maxn];//一侧的点数
int tol;
void addedge(int u,int v,int w){
edge[tol].u=u;
edge[tol].v=v;
edge[tol++].w=w;
}
bool cmp(Edge a,Edge b){
return a.w<b.w;
}
int findx(int x){
return F[x]==-1?x:F[x]=findx(F[x]);
}
long long Kruskal(int n){
memset(F,-1,sizeof(F));
sort(edge,edge+tol,cmp);
int cnt=0;
long long ans=0;
for(int i=0;i<tol;i++){
int u=edge[i].u;
int v=edge[i].v;
int w=edge[i].w;
int t1=findx(u);
int t2=findx(v);
if(t1!=t2){
ans+=(long long)w;//虽然不知道需不需要,但是还是写了转化,确保万无一失
D[v].push_back(Dis{u,w});
D[u].push_back(Dis{v,w});
F[t1]=t2;
cnt++;
}
if(cnt==n-1)break;
}
if(cnt<n-1)return -1;//不连通
else return ans;
}
void dfs(int n,int root,int father){
sum[root]=1;
for(int i=0;i<(int)D[root].size();i++){
int son=D[root][i].v;
int len=D[root][i].w;
if(son==father)continue;
dfs(n,son,root);
sum[root]+=sum[son];
dp[root]+=dp[son]+((double)sum[son]*(n-sum[son]))*len;
}
}
void init(int n){
memset(sum,0,sizeof(sum));
memset(dp,0,sizeof(dp));
memset(edge,0,sizeof(edge));
for(int i=1;i<=n;i++){
D[i].clear();
}
}
int main()
{
//freopen("in.txt","r",stdin);
//freopen("out.txt","w",stdout);
int t;
scanf("%d",&t);
while(t--){
int n,m;
scanf("%d%d",&n,&m);
tol=0;
init(n);
for(int i=0;i<m;i++){
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
addedge(u,v,w);
}
//这块地方类型转换需要特别注意
long long ans=Kruskal(n);
dfs(n,1,-1);
long long s=(long long)n*(n-1)/2;
printf("%I64d %.2f\n",ans,dp[1]/(double)s);
}
return 0;
}
Author : tak_fate@Acpp