[ZJOI2015] 地震后的幻想乡(状压dp + 期望)

problem

luogu-P3343

solution

d p ( i ) : dp(i): dp(i): 当恰好加入第 i i i 小边时候,所有点联通的方案数。

a n s = ∑ i d p i ( m i ) i m + 1 ans=\sum_i \frac{dp_i}{\binom mi}\frac{i}{m+1} ans=i(im)dpim+1i

重点是如何计算出 d p ( i ) dp(i) dp(i)

这个恰好的限制不好搞。加第 i i i 小的边之前不能提前联通,加后一定要联通。

转化一下, d p ( i ) = dp(i)= dp(i)= 加之前不连通的方案数 − - 加之后不连通的方案数。

f ( s , i ) : s f(s,i):s f(s,i):s 点集中的点之间连了 i i i 条边后 s s s 中的点仍然不连通的方案数。

g ( s , i ) : s g(s,i):s g(s,i):s 点集中的点之间连了 i i i 条边后 s s s 中的点联通的方案数。

显然 f ( s , i ) + g ( s , i ) = ( c n t ( s ) i ) f(s,i)+g(s,i)=\binom{cnt(s)}{i} f(s,i)+g(s,i)=(icnt(s)),其中 c n t ( s ) : cnt(s): cnt(s): 点集 s s s 中的点之间的边数。

转移枚举 s s s 的子集 t t t,满足 s & ( − s ) s\&(-s) s&(s),即 s s s 中最小元素点也在 t t t 集合内。
f ( s , i ) = ∑ ( s & − s ) ∈ t ⊂ s ∑ j = 0 i g ( t , j ) ( s ⊕ t i − j ) f(s,i)=\sum_{(s\&-s)\in t\subset s}\sum_{j=0}^ig(t,j)\binom{s\oplus t}{i-j} f(s,i)=(s&s)tsj=0ig(t,j)(ijst)
这种转移相当于以 s s s 中最小元素点为参考,其所在的联通块为 t t t,其余看作不连通,且 ( s ⊕ t i − j ) \binom{s\oplus t}{i-j} (ijst) 任意选的边连出来的集合一定不与 t t t 有边相连。这样就不会算重了。

最后 d p i = f ( { 1 , 2 , . . . , n } , i − 1 ) − f ( { 1 , 2 , . . . , n } , i ) dp_i=f(\{1,2,...,n\},i-1)-f(\{1,2,...,n\},i) dpi=f({1,2,...,n},i1)f({1,2,...,n},i)

在代码实现中统计答案略有不同,我是将这个式子稍微展开了一下。
a n s = ∑ i = 1 m i m + 1 f ( { 1 , 2 , . . . , n } , i − 1 ) − f ( { 1 , 2 , . . . , n } , i ) ( m i ) ans=\sum_{i=1}^m\frac{i}{m+1}\frac{f(\{1,2,...,n\},i-1)-f(\{1,2,...,n\},i)}{\binom mi} ans=i=1mm+1i(im)f({1,2,...,n},i1)f({1,2,...,n},i)
考虑相邻两位:
i m + 1 f ( { 1 , 2 , . . . , n } , i − 1 ) − f ( { 1 , 2 , . . . , n } , i ) ( m i ) i + 1 m + 1 f ( { 1 , 2 , . . . , n } , i ) − f ( { 1 , 2 , . . . , n } , i + 1 ) ( m i + 1 ) \frac{i}{m+1}\frac{f(\{1,2,...,n\},i-1)-f(\{1,2,...,n\},i)}{\binom mi}\\ \frac{i+1}{m+1}\frac{f(\{1,2,...,n\},i)-f(\{1,2,...,n\},i+1)}{\binom m{i+1}} m+1i(im)f({1,2,...,n},i1)f({1,2,...,n},i)m+1i+1(i+1m)f({1,2,...,n},i)f({1,2,...,n},i+1)
1 m + 1 \frac{1}{m+1} m+11 提出来,发现 i ∗ − f ( { 1 , 2 , . . . , n } , i ) i*-f(\{1,2,...,n\},i) if({1,2,...,n},i) ( i + 1 ) ∗ + f ( { 1 , 2 , . . . , n } , i ) (i+1)*+f(\{1,2,...,n\},i) (i+1)+f({1,2,...,n},i),相加其实就是加入了一次 f ( { 1 , 2 , . . . , n } , i ) f(\{1,2,...,n\},i) f({1,2,...,n},i)

f ( { 1 , 2 , . . . , n } , m ) = 0 f(\{1,2,...,n\},m)=0 f({1,2,...,n},m)=0,不可能所有边都加完了还不联通。

所以,有:
a n s = 1 m + 1 ∑ i = 0 m f ( { 1 , 2 , . . . , n } , i ) ( m i ) ans=\frac{1}{m+1}\sum_{i=0}^m\frac{f(\{1,2,...,n\},i)}{\binom mi} ans=m+11i=0m(im)f({1,2,...,n},i)

code

#include <bits/stdc++.h>
using namespace std;
#define double long double
#define int long long
int n, m;
int cnt[1 << 10];
double c[50][50];
int f[1 << 10][50], g[1 << 10][50];
struct node { int u, v; }E[50];

signed main() {
    scanf( "%lld %lld", &n, &m );
    for( int i = 1;i <= m;i ++ ) 
        scanf( "%lld %lld", &E[i].u, &E[i].v );
    for( int s = 0;s < (1 << n);s ++ )
        for( int i = 1;i <= m;i ++ )
            if( (s >> E[i].u - 1 & 1) and (s >> E[i].v - 1 & 1) )
                cnt[s] ++;
    for( int i = 0;i <= m;i ++ ) {
        c[i][0] = c[i][i] = 1;
        for( int j = 1;j < i;j ++ ) 
            c[i][j] = c[i - 1][j - 1] + c[i - 1][j];
    }
    for( int s = 0;s < (1 << n);s ++ )
        for( int i = 0;i <= cnt[s];i ++ ) {
            for( int t = s;t;t = (t - 1) & s )
                if( t & (s & -s) )
                    for( int j = 0;j <= min( cnt[t], i );j ++ )
                        f[s][i] += g[t][j] * c[cnt[s ^ t]][i - j];
            g[s][i] = c[cnt[s]][i] - f[s][i];
        }
    double ans = 0;
    for( int i = 0;i <= m;i ++ ) 
        ans += 1.0 / (m + 1) * f[(1 << n) - 1][i] / c[m][i];
    printf( "%Lf\n", ans );
    return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值