http://acm.hdu.edu.cn/showproblem.php?pid=5723
最小生成树+dfs
需要结论:
给定一棵树,求树上所有点对的距离之和,则树上的每一条边对这个和的贡献就是这条边两端的点的个数的乘积再乘上该边的边长(例如一条树边为w,该边一端点的个数为A,另一端的点的个数为B,则这条边的贡献值就是A×B×W),把所有的A;×B×W累加起来就是所有点对的距离之和。
补充结论:
树上任意点对的距离=两点到根节点的距离之和-两点最近公共祖先到根节点的距离×2;
做法:
在用kruskal生成最小生成树时,建立无环图,dfs求解每条树边的A,B,W,累加;
数据较大,把所有int替换long long 就过了。
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <queue>
#include <map>
#include <string>
#include <cmath>
#define maxn 1100000
#define LL long long
using namespace std;
struct node
{
LL u,v,w;
friend bool operator<(node a,node b)
{
return a.w<b.w;
}
}rd[maxn];
struct dot
{
LL v,w;
};
vector<dot>head[maxn/10];
LL father[maxn/10];
double dis_sum;
LL _find(LL x)
{
LL t=x;
while(x!=father[x])
x=father[x];;
while(t!=father[t])
{
LL temp=t;
t=father[t];
father[temp]=x;
}
return x;
}
bool join(LL x,LL y,LL w)
{
LL tx=x,ty=y;
x=_find(x);
y=_find(y);
if(x>y)
swap(x,y);
if(x!=y)
{
father[x]=y;
head[tx].push_back({ty,w});
head[ty].push_back({tx,w});
return true;
}
return false;
}
bool vis[maxn/10];
LL dfs(LL rt,LL dis,LL n)
{
vector<dot>& vec=head[rt];
LL num=1;
for(LL i=0,len=vec.size();i<len;++i)
{
LL v=vec[i].v;
LL w=vec[i].w;
if(!vis[v])
{
vis[v]=true;
num+=dfs(v,w,n);
}
}
dis_sum+=(num*(n-num)*dis);
return num;
}
int main()
{
LL T;
scanf("%lld",&T);
while(T--)
{
LL n,m;
scanf("%lld%lld",&n,&m);
for(LL i=0;i<m;++i)
{
scanf("%lld%lld%lld",&rd[i].u,&rd[i].v,&rd[i].w);
}
sort(rd,rd+m);
for(LL i=0;i<=n;++i)
{
head[i].clear();
father[i]=i;
}
LL dis=0;
for(LL i=0;i<m;++i)
{
if(join(rd[i].u,rd[i].v,rd[i].w))
{
dis+=rd[i].w;
}
}
memset(vis,false,sizeof(vis));
dis_sum=0;
vis[1]=true;
dfs(1,0,n);
dis_sum/=((double)n-1)*n/2;
printf("%lld %.2lf\n",dis,dis_sum);
}
}