Description
给出一张n个点m条边的无向图,第i条边有一个在[0,1]内独立随机的权值ei
问这张图的最小生成树的最大边权的期望
n<=10,m<=n*(n-1)/2
Solution
设f(x)表示只用边权<x的边,图不连通的概率
可以发现,f(k)=P(x>=k),根据概率公式我们知道答案是
∫
0
1
f
(
x
)
d
x
\int_0^1f(x)dx
∫01f(x)dx
显然f(x)是一个关于x的多项式,我们考虑怎么求
设F[s]表示点集S连通的概率,这个很经典,可以枚举包含S中任意一个点的子集来转移
那么答案就是1-F[all]
注意这种做法会被卡精度,要用__float128
Code
#include <vector>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
using namespace std;
typedef vector<__float128> poly;
poly operator + (poly a,poly b) {
poly c;c.resize(max(a.size(),b.size()));
for(int i=0;i<c.size();i++) c[i]=a[i]+b[i];
return c;
}
poly operator - (poly a,poly b) {
poly c;c.resize(max(a.size(),b.size()));
for(int i=0;i<c.size();i++) c[i]=0;
for(int i=0;i<a.size();i++) c[i]+=a[i];
for(int i=0;i<b.size();i++) c[i]-=b[i];
while (!c.empty()&&!c[c.size()-1]) c.pop_back();
return c;
}
poly operator * (poly a,poly b) {
poly c;c.resize(a.size()+b.size()-1);
for(int i=0;i<c.size();i++) c[i]=0;
for(int i=0;i<a.size();i++)
for(int j=0;j<b.size();j++)
c[i+j]+=a[i]*b[j];
return c;
}
const int S=(1<<10)+5,N=11,M=N*N;
poly f[S],pw[M];
int n,m,x,y,e[N];
int count(int s) {
int ret=0;
for(;s;s>>=1) ret+=s&1;
return ret;
}
int main() {
scanf("%d%d",&n,&m);
fo(i,1,m) {
scanf("%d%d",&x,&y);
e[x]|=1<<y-1;e[y]|=1<<x-1;
}
pw[0].resize(1);pw[0][0]=1;
pw[1].resize(2);pw[1][0]=1;pw[1][1]=-1;
fo(i,2,m) pw[i]=pw[i-1]*pw[1];
fo(s,1,(1<<n)-1) {
f[s].resize(1);f[s][0]=1;
int x=s&-s,tw=s-x;
for(int t=tw;t;t=(t-1)&tw) {
if ((t|x)==s) continue;
int cnt=0;
fo(i,1,n) if ((t|x)&(1<<i-1)) cnt+=count(e[i]&(s^(t|x)));
f[s]=f[s]-f[t|x]*pw[cnt];
}
if (x==s) continue;
int cnt=0;
fo(i,1,n) if (x&(1<<i-1)) cnt+=count(e[i]&(s^x));
f[s]=f[s]-f[x]*pw[cnt];
}
poly ans;ans.resize(1);ans[0]=1;
ans=ans-f[(1<<n)-1];
__float128 ret=0;
for(int i=0;i<ans.size();i++) ret+=ans[i]*1.0/(i+1);
printf("%.6lf\n",(double)ret);
return 0;
}