zoukankan      html  css  js  c++  java
  • ZJOI2015 地震后的幻想乡

    ZJOI2015 地震后的幻想乡

    我们其实只需要边的相对的发小关系,我们只要知道这个边是第几就可以了,因为如果知道它是第几就知道权值期望是 $ frac i {m+1} $

    所以我们考虑这样一个dp, $ dp[S][i] $ 表示加入第 $ i $ 小的边权时 $ S $ 是联通的方案数。

    由于一个显然的前缀和,我们知道答案其实就是

    [sum_i frac i {m+1} imes ( frac {dp[S][i]}{inom i m} - frac{dp[S][i-1]}{inom {i - 1} m} ) ]

    但是这个 $ dp $ 咋推呢?我们可以类似 HDU5552 的做法来推,一样是算联通图的数量,所以可以一样算不联通图的数量,考虑 $ f[S][i] $ 表示 $ S $ 加入 $ i $ 边后不联通图的方案数量。

    不联通图的方案可以一样套用那个题的做法,考虑固定一个点,并且由这个点连出一个联通图,并且剩下的点自己连,和这个点的联通图没得关系。

    由于最后连成的图肯定是包含这个点的,所以可以不重不漏统计所有方案。于是我们可以写出方程:

    [f[S][i] = dp[T][j] + inom {d[S ackslash T]} {i-j} ]

    其中 $ T $ 是 $ S $ 的一个子集,并且固定的这个点在 $ T $ 里面, $ d[S] $ 表示 $ S $ 点集在图里的边的数量。

    而且 $ f $ 和 $ dp $ 明显是互补的,所以和为 $ inom {d[S]} {i} $。

    这题就做完啦。

    但是想想组合数可不可能太大导致爆炸了呢?发现这题组合数最大也就 $ C_{50}^{10} $ 这个级别(其实更小),才 $ 10^{11} $ 左右,很稳。

    #include "iostream"
    #include "algorithm"
    #include "cstring"
    #include "cstdio"
    #include "vector"
    using namespace std;
    #define int long long
    #define MAXN 12
    #define chkmn( a , b ) ( (a) > (b) ? (a) = (b) , 1 : 0 )
    #define chkmx( a , b ) ( (a) < (b) ? (a) = (b) , 1 : 0 )
    int n , m;
    long long dp[MAXN * MAXN][1 << MAXN] , f[MAXN * MAXN][1 << MAXN] , d[1 << MAXN];
    vector<int> G[MAXN];
    long long C[51][51];
    signed main() {
        cin >> n >> m;
        C[0][0] = 1;
        for( int i = 1 ; i <= m ; ++ i ) {
            C[i][0] = 1;
            for( int j = 1 ; j <= i ; ++ j ) C[i][j] = C[i - 1][j - 1] + C[i - 1][j];
        }
        for( int i = 1 , u , v ; i <= m ; ++ i ) {
            scanf("%lld%lld",&u,&v); G[u].push_back( v );
        }
        for( int i = 1 ; i < ( 1 << n ) ; ++ i)
            for( int j = 1 ; j <= n ; ++ j ) if( i & ( 1 << j - 1 ) )
                    for( int v : G[j] ) if( i & ( 1 << v - 1 ) )
                            ++ d[i];
        for( int i = 1 ; i <= m ; ++ i ) dp[i][0] = 1;
        for( int s = 1 ; s < ( 1 << n ) ; ++ s ) {
            int ps = 0;
            for( int j = 1 ; j <= n ; ++ j ) if( s & ( 1 << j - 1 ) ) ps = j;
            for( int i = 0 ; i <= d[s] ; ++ i ) {
                for( int t = ( s - 1 ) & s ; t != s ; t = ( t - 1 ) & s ) {
                    if( ~t & ( 1 << ps - 1 ) ) continue;
                    for( int j = 0 ; j <= min( d[t] , i ) ; ++ j ) {
                        f[i][s] += dp[j][t] * C[d[s ^ t]][i - j];
                    }
                }
                dp[i][s] = C[d[s]][i] - f[i][s];
            }
        }
        double res = 0.0;
        for( int i = 1 ; i <= m ; ++ i )
            res += 1.0 * i / ( m + 1 ) * ( 1.0 * dp[i][(1<<n) - 1] / C[m][i] - 1.0 * dp[i-1][(1<<n)-1] / C[m][i - 1] );
        printf("%.6lf",res);
    }
    
  • 相关阅读:
    python: 字符串按空格分成列表split与加密密码maketrans
    android 几个工具方法
    华硕N43sl VNP 连接问题 800 807 621
    git 创建新项目,下载工程,合并和更新工程简单应用记录
    android分析windowManager、window、viewGroup之间关系(二)
    android分析windowManager、window、viewGroup之间关系(一)
    meta-data获取小结
    转载: 项目经验分享
    不想写代码了
    MapReduce源码分析总结(转)
  • 原文地址:https://www.cnblogs.com/yijan/p/12323296.html
Copyright © 2011-2022 走看看