又查错了一下午。。666.
题目 :http://acm.hdu.edu.cn/showproblem.php?pid=5723
工具 : 1.并查集(压缩路径)2.优先队列 3.向量(Vector).
解决这个问题分两步:
1.简单的最小生成树,计算路径.。直接用kruskal()可以解决;
2.建树之后(前提很重要),求出 任意两点的距离的总和,然后除以所有可能组合方式:n*(n-1)/2。A
求任意两点距离(难点):任一条路径,只需计算它被走过了次数,就可以求出所有的距离。
而走过的次数:只需要知道一条路径的两个端点A,B,以一个端点A为父节点,计算不经过另一个端点B的所有子节点的个数(包括自己),得到点的个数为NA。
再通过节点总数N,由 NB=N-NA;得到B端的节点数目。所以走过次数为(NA*NB);具体算法见题解。MST();
此外就是一些细节处理,主要是记得每条路径可以为 1000000,因此需要开 long long.
#include<cstdio>
#include<queue>
#include<vector>
#include<cstring>
using namespace std;
struct node
{
int from,to,cost;
friend bool operator <(const node&a,const node&b)
{
return a.cost>b.cost;
}
}line[100005],l;
struct mst
{
int to,cost;
mst(int t,int c)
:to(t),cost(c){}
};
int pre[100005],sum[1000005],n,m;
long long dist[100005];
priority_queue<node>q;
vector<mst>vec[100005];
int find(int x) //并查集
{
int r=x;
while(r!=pre[r])
r=pre[r];
int i=x,j;
while(r!=pre[i])
{
j=pre[i];
pre[i]=r;
i=j;
}
return r;
}
long long kruskal() //得最小生成树权值
{
long long ans=0;
for(int i=1;i<=n;++i)
pre[i]=i,vec[i].clear();
int tmp=0 ;
while(!q.empty()&&tmp!=n-1)
{
l=q.top();
q.pop();
int ff=find(l.from);
int ft=find(l.to);
if(ff!=ft)
{
tmp++;
pre[ft]=ff;
ans+=l.cost;
vec[l.from].push_back(mst(l.to,l.cost));
vec[l.to].push_back(mst(l.from,l.cost));
}
}
return ans;
}
void dfs(int From,int To) //核心 : 计算每条边走过次数,并计算出距离;
{
sum[From]=1;
for(int i=0;i<(int)vec[From].size();++i)
{
if(vec[From][i].to==To)continue;
dfs(vec[From][i].to,From);
sum[From]+=sum[vec[From][i].to];
dist[From]+=dist[vec[From][i].to]+((long long)sum[vec[From][i].to]*(n-sum[vec[From][i].to])*vec[From][i].cost);
}
}
double MST()
{
double ans=0;
memset(dist,0,sizeof(dist));
dfs(1,-1);
long long s=(long long)n*(n-1)/2;
return dist[1]*1.0/s;
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
while(!q.empty())
q.pop();
scanf("%d%d",&n,&m);
for(int i=0;i<m;++i)
{
scanf("%d%d%d",&l.from,&l.to,&l.cost);
q.push(l); //获得路径并排序;
}
printf("%lld ",kruskal());
printf("%.2lf\n",MST());
}
}