题意:有n个城市,m条需要被重建的路,每条路有权值且各不相同,让你使所有城市相通(直接或间接)的情况下,找出权值和最小的那种方案,然后问你从这种方案中任意选取两点,问这两点之间的距离的最小期望值是多少。
解题思路:让你找出权值和最小,当然是最小生成树啊,但是后面那个最小期望有点麻烦,不过我们可以仔细分析得到,每条路的权值都不一样所得出的最小生成树是唯一的(具体证明自己百度),那么生成树唯一,怎样在这棵树上求最小期望,最小期望?这其实是出题人忽悠你的,一棵树上任意两点之间的距离唯一,所以不存在最小最大之说,只要找出任意两两点之间的距离就行,然后把所有的距离加起来除以n*(n - 1)/2就行,那么问题就转化为在一无根树上求任意两点之间的距离之和,我们分析可以得到,所有距离之和等于每一条边的权值乘以这条边出现的次数之和,每一条边的权值我们知道,我们现在要知道每一条边出现过多少次,而我们可以发现,每条边出现的次数等于这条边的两个端点两侧`顶点数目的乘积,知道了这一点,我们就可以直接dfs了,任意选取一个顶点为根(我这里选的是1),令dp[u]等于以u为根的子树的顶点数,然后枚举每一条边,假如一条边的两个顶点是x1,x2,那么x1,x2中要么x1是x2的父亲,要么x2是x1的父亲,那么到底谁是谁的父亲,很简单,比较dp[x1],dp[x2],小的那个一定是孩子,所有这条边的一侧的顶点数为v1 = min(dp[x1],dp[x2]),那么另一侧顶点数是多少呢?很简单,总数是n ,所有另一侧顶点数v2 = n - v1,所有这条边出现的次数为v1v2,注意最后求期望除以n*(n - 1)/2时一定要将n,n - 1变成long long 之后再求,我在这里wa了无数次。
代码:
#include<stdio.h> #include<iostream> #include<algorithm> #include<vector> #include<string.h> typedef long long ll; using namespace std; const int max_=1e6+5; const int max_n=1e5+5; struct edge{ int form; int to; ll w; }; struct edge tu[max_],tree[max_n]; int dp[max_n];//记录包自身的子树节点 int fa[max_n];//父节点 bool vis[max_n];//标记数组 vector<int>g[max_n];//记录子节点 int tot1,tot2; void add_edge1(int x,int y,ll w)//建图 { tu[tot1].form=x; tu[tot1].to=y; tu[tot1++].w=w; } void add_edge2(int x,int y,ll w)//建树 { tree[tot2].form=x; tree[tot2].to=y; tree[tot2++].w=w; } bool cmp(edge a,edge b)//排序 { return a.w<b.w; } int find_fa(int x)//并查集 { if(x==fa[x]) return x; else return fa[x]=find_fa(fa[x]); } ll Kruskal(int n)//最小生成树算法 { sort(tu,tu+tot1,cmp); int ans=0; ll ant=0; for(int i=0;i<tot1;i++) { int v=tu[i].form; int u=tu[i].to; ll w=tu[i].w; int f1=find_fa(v); int f2=find_fa(u); if(f1!=f2) { fa[f1]=f2; ant+=w; add_edge2(u,v,w); ans++; g[u].push_back(v); g[v].push_back(u); } if(ans==n-1) { return ant; } } } void dfs(int x)//dfs寻找树的子节点 { vis[x]=true; dp[x]=1; for(int i=0;i<g[x].size();i++) { int y=g[x][i]; if(!vis[y]) { dfs(y); dp[x]+=dp[y]; } } } void init(int n)//初始化 { memset(vis,0,sizeof(vis)); memset(dp,0,sizeof(dp)); for(int i=1;i<=n;i++) { fa[i]=i; g[i].clear(); } tot1=0; tot2=0; } int main() { int t; scanf("%d",&t); while(t--) { int n,m; scanf("%d %d",&n,&m); ll ans=0; init(n); if(m==0) { printf("0 0.00\n"); continue; } while(m--) { int x,y; ll w; scanf("%d %d %lld",&x,&y,&w); add_edge1(x,y,w); } ll sum=Kruskal(n); dfs(1); for(int i=0;i<tot2;i++) { int u=tree[i].form; int v=tree[i].to; ll w=tree[i].w; int k=min(dp[v],dp[u]); ans+=w*(n-k)*k; //cout<<ans<<endl; } // printf("%.2f\n",1.0*(n-1)*n/2); double expe=ans*1.0/(1.0*n*(n-1)/2); printf("%lld %.2lf\n",sum,expe); } }