题意
有一个n个点m条边的图,每条边有长度和美丽值。求该图的所有最小生成树中美丽值的和的期望。
满足长度相同的边的数量不超过30。
n
≤
10000
,
m
≤
200000
n\le10000,m\le200000
n≤10000,m≤200000
分析
显然长度不同的边的贡献是独立的。
那么我们可以把每一种距离的边拿出来,对每一个连通块分别处理。
枚举同一个连通块中的每一条边,用矩阵树定理算出一定包含这条边的最小生成树的数量,然后算期望即可。
求行列式的时候不能辗转相除,要用long double。
代码
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<vector>
#include<cmath>
#define pb push_back
typedef long double LL;
const int N=10005;
const LL eps=1e-12;
int n,m,f[N],id[N],g[N],num[N];
struct edge{int x,y,d,v;}e[N*20];
LL ans,a[35][35];
int find(int x)
{
if (f[x]==x) return x;
else return f[x]=find(f[x]);
}
int get(int x)
{
if (g[x]==x) return x;
else return g[x]=get(g[x]);
}
bool cmp(edge a,edge b)
{
return a.d<b.d;
}
bool cmpf(int x,int y)
{
return find(e[x].x)<find(e[y].x);
}
LL det(int n)
{
for (int i=1;i<=n;i++)
{
int l=i;
for (int j=i;j<=n;j++) if (a[j][i]) {l=j;break;}
if (l!=i) for (int j=i;j<=n;i++) std::swap(a[l][j],a[i][j]);
for (int j=i+1;j<=n;j++)
if (fabs(a[j][i])>eps)
{
LL w=a[j][i]/a[i][i];
for (int k=i;k<=n;k++) a[j][k]-=a[i][k]*w;
}
}
LL ans=1;
for (int i=1;i<=n;i++) ans=(LL)ans*a[i][i];
return ans<0?-ans:ans;
}
void solve(int l,int r)
{
int tot=0;
for (int i=l;i<=r;i++)
{
int x=get(e[id[i]].x),y=get(e[id[i]].y);
if (!num[x]) num[x]=++tot;
if (!num[y]) num[y]=++tot;
x=num[x];y=num[y];
a[x][x]+=1;a[y][y]+=1;a[x][y]-=1;a[y][x]-=1;
}
LL ss=det(tot-1);
for (int i=1;i<=tot;i++)
for (int j=1;j<=tot;j++)
a[i][j]=0;
for (int i=l;i<=r;i++) num[get(e[id[i]].x)]=num[get(e[id[i]].y)]=0;
for (int k=l;k<=r;k++)
{
int p=get(e[id[k]].x),q=get(e[id[k]].y);
tot=0;
for (int i=l;i<=r;i++)
{
int x=get(e[id[i]].x),y=get(e[id[i]].y);
if (x==y||x==p&&y==q||x==q&&y==p) continue;
if (x==p) x=q;
if (y==p) y=q;
if (!num[x]) num[x]=++tot;
if (!num[y]) num[y]=++tot;
x=num[x];y=num[y];
a[x][x]+=1;a[y][y]+=1;a[x][y]-=1;a[y][x]-=1;
}
LL tt=det(tot-1);
ans+=(LL)1.0*tt/ss*e[id[k]].v;
for (int i=1;i<=tot;i++)
for (int j=1;j<=tot;j++)
a[i][j]=0;
for (int i=l;i<=r;i++) num[get(e[id[i]].x)]=num[get(e[id[i]].y)]=0;
}
}
int main()
{
scanf("%d%d",&n,&m);
for (int i=1;i<=m;i++) scanf("%d%d%d%d",&e[i].x,&e[i].y,&e[i].d,&e[i].v);
std::sort(e+1,e+m+1,cmp);
for (int i=1;i<=n;i++) f[i]=g[i]=i;
for (int l=1;l<=m;l++)
{
int r=l;
while (r<m&&e[r+1].d==e[r].d) r++;
for (int i=l;i<=r;i++)
{
int x=e[i].x,y=e[i].y;
if (find(x)!=find(y)) f[find(x)]=find(y);
}
int tot=0;
for (int i=l;i<=r;i++) if (get(e[i].x)!=get(e[i].y)) id[++tot]=i;
std::sort(id+1,id+tot+1,cmpf);
for (int p=1;p<=tot;p++)
{
int q=p;
while (q<tot&&find(e[id[q+1]].x)==find(e[id[q]].x)) q++;
solve(p,q);
p=q;
}
for (int i=l;i<=r;i++)
{
int x=e[i].x,y=e[i].y;
if (get(x)!=get(y)) g[get(x)]=get(y);
}
l=r;
}
printf("%.5lf\n",(double)ans);
return 0;
}