测试地址:地震后的幻想乡
做法:本题需要用到概率期望+子集状压DP。
题目要求最小生成树最大边的期望,我们知道这个值等于最大边的期望排名(从小到大)
/(m+1)
/
(
m
+
1
)
,因为提示里说了,
m
m
个内的随机变量的第
k
k
小值的期望为。
那么令
L
L
为最大边的排名,则有:
得到这个和式,我们变换它的求和顺序,可得:
ans=∑mx=1P(L≥x)
a
n
s
=
∑
x
=
1
m
P
(
L
≥
x
)
而最大边
≥x
≥
x
的概率,等于用排名前
x−1
x
−
1
的边不能使图连通的概率,令这个概率为
T(x−1)
T
(
x
−
1
)
,我们要求:
ans=∑m−1x=0T(x)
a
n
s
=
∑
x
=
0
m
−
1
T
(
x
)
因为每种
x
x
条边的组合作为排名前的边的概率是相等的,于是
T(x)
T
(
x
)
实际上就等于,用
x
x
条边使得图不能连通的方案数,除以所有条边的组合数。于是问题变成了如何求用
x
x
条边使得图不能连通的方案数。
于是我们有了一个定义状态的思路:令为用(连接点集
i
i
内部点的)条边使得点集
i
i
不连通的方案数。我们发现只有这个状态不好转移,因为一般这种连通性状压DP的转移思路都是,通过枚举某个点所在的连通块,来做到不重不漏地枚举。于是我们有另一个状态定义:令为用(连接点集
i
i
内部点的)条边使得点集
i
i
连通的方案数,那么令为
i
i
的一个包含某定点的真子集,有以下状态转移方程:
f(i,j)=∑k∑edge(k)l=0g(k,l)⋅Cj−ledge(i−k)
f
(
i
,
j
)
=
∑
k
∑
l
=
0
e
d
g
e
(
k
)
g
(
k
,
l
)
⋅
C
e
d
g
e
(
i
−
k
)
j
−
l
其中
edge(S)
e
d
g
e
(
S
)
表示连接点集
S
S
内部点的边的数量,那么上式的含义其实就是,枚举定点所在的连通块,求剩下的点集不和该连通块连接的方案数。而我们发现
f
f
和的定义是互补的,所以有:
g(i,j)=Cjedge(i)−f(i,j)
g
(
i
,
j
)
=
C
e
d
g
e
(
i
)
j
−
f
(
i
,
j
)
这样我们就可以同时转移
f
f
和了。那么令
all
a
l
l
为全集,答案就为:
ans=1m+1∑m−1x=0f(all,x)Cxedge(all)
a
n
s
=
1
m
+
1
∑
x
=
0
m
−
1
f
(
a
l
l
,
x
)
C
e
d
g
e
(
a
l
l
)
x
于是我们就解决了这一题,时间复杂度为
O(3nn2)
O
(
3
n
n
2
)
。
以下是本人代码:
#include <bits/stdc++.h>
using namespace std;
int n,m,a[60],b[60],edge[1210];
double C[60][60]={0},f[1210][60],g[1210][60];
void init()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)
scanf("%d%d",&a[i],&b[i]);
C[0][0]=1.0;
for(int i=1;i<=m;i++)
{
C[i][0]=1.0;
for(int j=1;j<=i;j++)
C[i][j]=C[i-1][j-1]+C[i-1][j];
}
for(int i=1;i<(1<<n);i++)
{
edge[i]=0;
for(int j=1;j<=m;j++)
if ((i&(1<<(a[j]-1)))&&(i&(1<<(b[j]-1))))
edge[i]++;
}
}
void work()
{
for(int i=1;i<(1<<n);i++)
{
for(int j=0;j<=edge[i];j++)
{
f[i][j]=0.0;
int lowbit=i&(-i);
for(int k=((i-1)&i);k;k=((k-1)&i))
if (k&lowbit)
{
for(int l=0;l<=edge[k]&&l<=j;l++)
f[i][j]+=g[k][l]*C[edge[i-k]][j-l];
}
g[i][j]=C[edge[i]][j]-f[i][j];
}
}
double ans=0.0;
for(int i=0;i<m;i++)
ans+=f[(1<<n)-1][i]/C[edge[(1<<n)-1]][i];
ans/=(double)(m+1);
printf("%.6lf",ans);
}
int main()
{
init();
work();
return 0;
}