BZOJ 3925 ZJOI2015 地震后的幻想乡 状压dp+期望

题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=3925


题意概述:

给出一张N点M边的最小生成树,其中每条边的长度为[0,1]的实数,求最小生成树中最大边的期望大小是多少。
N<=10,M<=N(N-1)/2.

由于本人实在太弱所以细节会描述的比较多。(精简版本见下方)

整理一下手里有的信息:

1.题目告诉我们N个大小在[0,1]的随机变量中第i大的期望大小为i/(N+1)。

2.由于两个[0,1]的随机变量大小相同的概率为0(小到为0),所以说只考虑每个随机变量大小不同的情况就可以了。

3.在所有边不同的条件下,在做最小生成树的时候第k条考察的边就是所有边中第k小的边(kruskal算法)。

所以如果知道图中最后一条加入最小生成树的边为所有边中第i大的概率p[i],那么答案就可以表示为:

ans=sum{ p[i] * i/(M+1) | 1<=i<=M }

根据信息3,可以发现p[i]实际上也就是第i条考察的边加入后恰好使得图连通的概率(注意i不是按照给出的边的顺序来考察的)。这又转化为了一个图的连通性问题,只要我们知道一个图中选择i条边使得图连通的概率xi,那么p[i]=xi-xi-1就是在第i条边加入之后图恰好连通的概率(因为i-1->i有两种可能,i-1就是连通的或者i-1是不连通的,所以是减法)。
好像某人说过“概率说到底还是一个组合计数问题”(离散概率),我们把这个概率xi用方案数来表示(注意到M<=45所以用C估计一下算出来的方案数的范围,并没有毛病)。设g(i)表示用i条边将图连通的方案数,则xi=g(i)/C(M,i)。(i条边使图连通的概率=i条边令图连通的方案数/选出i条边的方案数)

于是ans=sum{ (g(i)/C(M,i) - g(i-1)/C(M,i-1)) * i/(M+1) | 1<=i<=M }

接下来求出g问题就解决了。我们设g(s,i)表示用i条边将点集s中的点连通的方案数,那么f(s,i)表示用i条边不能将点集s中的点连通的方案数,有g(s,i)=C(cnt[s],i)-f(s,i),cnt[x]表示两端都在点集x中的边的条数。对于每个f(s,i)任意选择s中的一个点x,f(s,i)=sum{ g(ss,ii)*C(cnt[s-ss],i-ii) | ss为s的真子集且包含x,sz[ss]-1<=ii<=min(i,cnt[ss]),i-ii<=cnt[s-ss] },sz[s]表示集合s中点的数量。(方程的正确性:对于任意一种不连通的情况,一定存在至少两个连通分量,找到一个连通分量并枚举其所有连通的情况,用其方案数乘上对应情况下其他所有点可能的形态方案数累加到答案中)

于是再整理一下:ans=sum{ (g(2N-1,i)/C(M,i) - g(2N-1,i-1)/C(M,i-1)) * i/(M+1) | 1<=i<=M }

实际上到这里就完了但是敏锐的人发现方程中的g换成f之后好像可以化简?!由于原图是连通的所以当选用M条边的时候f(2N-1,M)=0,这样可以把展开后的式子的最后一项剩余的消掉,最后答案写为:ans=sum{ f(2N-1,i)/C(M,i) | 1<=i<M } / (M+1)

是不是很顺理成章?但是真的很抱歉自己想我真的。。。。。。ORZ

精简版本:

g(s,i)表示用i条边将点集s中的点连通的方案数,f(s,i)表示用i条边不能将点集s中的点连通的方案数,有g(s,i)=C(cnt[s],i)-f(s,i),cnt[x]表示两端都在点集x中的边的条数。

令x为s中任意一个点,f(s,i)=sum{ g(ss,ii)*C(cnt[s-ss],i-ii) | ss为s的真子集且包含x,sz[ss]-1<=ii<=min(i,cnt[ss]),i-ii<=cnt[s-ss] },sz[s]表示集合s中点的数量。

ans = sum{ (g(2N-1,i)/C(M,i) - g(2N-1,i-1)/C(M,i-1)) * i/(M+1) | 1<=i<=M } = sum{ f(2N-1,i)/C(M,i) | 1<=i<M } / (M+1)

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<cstdlib>
 5 #include<algorithm>
 6 #include<cmath>
 7 #include<queue>
 8 #include<set>
 9 #include<map>
10 #include<vector>
11 #include<cctype>
12 using namespace std;
13 const int maxn=12;
14 const int maxm=50;
15 const int maxs=(1<<10)+5;
16 typedef long long LL;
17 
18 int N,M;
19 int bin[maxn],cnt[maxs],sz[maxs];
20 LL f[maxs][maxm],g[maxs][maxm],C[maxm][maxm];
21 struct edge{ int u,v; }E[maxm];
22 
23 void data_in()
24 {
25     scanf("%d%d",&N,&M);
26     int x,y;
27     for(int i=1;i<=M;i++){
28         scanf("%d%d",&x,&y);
29         E[i]=(edge){x,y};
30     }
31 }
32 void dp()
33 {
34     for(int i=1;i<=N+1;i++) bin[i]=1<<i-1;
35     for(int i=0;i<=M;i++){
36         C[i][0]=1;
37         for(int j=1;j<=i;j++)
38             C[i][j]=C[i-1][j]+C[i-1][j-1];
39     }
40     for(int s=1;s<bin[N+1];s++)
41     for(int i=1;i<=M;i++)
42         if((bin[E[i].u]&s)&&(bin[E[i].v]&s)) cnt[s]++;
43     for(int s=1;s<bin[N+1];s++){
44         sz[s]=sz[s>>1]+(s&1);
45         for(int i=0;i<=cnt[s];i++){
46             if(sz[s]==1) g[s][i]=1;
47             else{
48                 int x;
49                 for(x=1;x<=N;x++) if(bin[x]&s) break;
50                 for(int ss=(s-1)&s;ss;ss=(ss-1)&s) if(bin[x]&ss)
51                 for(int ii=sz[ss]-1;ii<=min(i,cnt[ss]);ii++)
52                     if(i-ii<=cnt[s^ss])
53                         f[s][i]+=g[ss][ii]*C[cnt[s^ss]][i-ii];
54                 g[s][i]=C[cnt[s]][i]-f[s][i];    
55             }
56         }
57     }
58 }
59 void work()
60 {
61     dp();
62     double ans=0;
63     int all=bin[N+1]-1;
64     for(int i=0;i<M;i++)
65         ans+=1.0*f[all][i]/C[cnt[all]][i];
66     printf("%.6f
",ans/(M+1));
67 }
68 int main()
69 {
70     data_in();
71     work();
72     return 0;
73 }
原文地址:https://www.cnblogs.com/KKKorange/p/8474320.html