zoukankan      html  css  js  c++  java
  • CCPC2020 威海 E.So Many Possibilities...

    CCPC2020 威海 E.So Many Possibilities...

    E

    题意:n 个怪物,第 i 个怪物有血量 (a_i) 。 m 次操作,每次从还活着(血量>0)的怪物中等概率随机挑选一个使其血量减一,问 m 次操作后期望杀死多少只怪物。 (n le 15, m,a_i le 100, sum a_i > m)

    key:概率 dp

    如果知道哪些死了,也知道操作数,那么没有针对最终死亡的怪物的操作次数也是可以知道的,并且实际上针对谁并不重要。也就是说,对于一个操作序列,我们只需要知道哪些操作是针对最终死亡的即可,剩下的可以直接乘一个系数。

    (g[i][S]) 表示对于 (j in S) 的所有怪物一共砍 i 刀且都不砍死的方案数。等价于 i 个石头染色,第 (j) 种颜色小于等于 (a_j-1) 块石头的方案数。暴力枚举 S 然后 dp 是 (O(2^nnm^2)) ,边 dfs 边 dp 就变成 (O(2^nm^2)) 。这个就是那个系数,即活着的怪物的贡献。

    现在考虑死掉的怪物的贡献。记 (S) 表示生死状态,0 为生, 1 为死。 (f[i][S]) 表示砍 i 刀使得生死状态为 S 的概率,(sum[S]) 表示 (jin S) 的所有怪物血量之和,那么其中 (i-sum[S]) 次操作是砍在活着的怪物上的,在这里贡献是 1 (因为当前只考了死掉的怪物)。那么第 i+1 刀有两种情况:

    1. 砍死一只新怪物,设其为 j 。那么 (i-sum[S]) 中应该有 (a_j-1) 次操作砍在 j 上。贡献是 ({i-sum[S] choose a_j-1} / |ar S|)
    2. 没有砍死。那么当前贡献是 (1/|ar S|)

    要除以 (|ar S|) 是因为第 i+1 次操作的随机性。复杂度是 (O(2^nnm))

    最终答案是 (sum_{S} f[m][S]*g[m-sum[S]][ar S]*|S|)

    于是总复杂度是 (O(2^n(n+m)m))

    #include<bits/stdc++.h>
    using namespace std;
    
    typedef long long LL;
    typedef long double LD;
    typedef pair<LL,LL> pii;
    typedef pair<double, double> pdd;
    const int SZ = 50100;
    const double INF = 1e10 + 10;
    const int mod = 998244353;
    const LD eps = 1e-8;
    
    LL read() {
        LL n = 0;
        char a = getchar();
        bool flag = 0;
        while(a > '9' || a < '0') { if(a == '-') flag = 1; a = getchar(); }
        while(a <= '9' && a >= '0') { n = n * 10 + a - '0',a = getchar(); }
    	if(flag) n = -n;
    	return n;
    }
    
    
    int n,m,a[117];
    double f[117][SZ],C[110][110];
    double g[117][110];
    double G[110][SZ];
    int sum[SZ];
    int NT[SZ];
    
    // 0:live  1:dead
    
    void print(int x) {
        for(int i = 0;i < n;i ++)
            printf("%d",x>>i&1);
        printf("");
    }
    
    
    void dfs(int d, int S, int nt) {
        if(d == n) {
            for(int i = 0;i <= m;i ++) {
                G[i][S] = g[nt][i];
            }
            return ;
        }
    
        for(int i = 0;i <= m;i ++) {
            for(int j = 0;j < a[d+1] && j <= i;j ++)
                g[nt+1][i] += g[nt][i-j] * C[i][j];
        }
        dfs(d+1,S,nt+1); // d is live
        for(int i = 0;i <= m;i ++) {
            g[nt+1][i] = 0;
        }
        dfs(d+1,S^(1<<d),nt);
    }
    
    void init() {
        C[0][0] = 1;
        for(int i = 1;i <= 100;i ++) {
            C[i][0] = 1;
            for(int j = 1;j <= i;j ++) {
                C[i][j] = C[i-1][j]+C[i-1][j-1];
            }
        }
    }
    
    bool isok(int i,int S) {
        int nS = (1<<n)-S-1;
        return i >= sum[S] && sum[nS] - NT[S] >= i-sum[S];
    }
    
    int main() {
        init();
        n = read(),m = read();
        for(int i = 1;i <= n;i ++) a[i] = read();
        for(int S = 0;S < (1<<n);S ++) {
            for(int i = 0;i < n;i ++) {
                if(S>>i&1)
                    sum[S] += a[i+1];
            }
        }
    
        g[0][0] = 1;
        dfs(0,0,0);
    
    
        for(int S = 0; S < (1<<n);S ++) { // S,t: dead   ns,nt: live
            int nt = 0;
            for(int i = 0;i < n;i ++) if(!(S>>i&1)) nt ++;
            NT[S] = nt;
        }
    
    
        // 0:live  1:dead
        double ans = 0;
        f[0][0] = 1;
        for(int S = 0; S < (1<<n);S ++) { // 1,t: dead   0,nt: live
            int nt = 0;
            for(int i = 0;i < n;i ++) if(!(S>>i&1)) nt ++;
            int t = n - nt;
            int nS = (1<<n)-S-1;
            for(int i = 0;i < m;i ++) {
                if(isok(i,S)) {
                    double x = 1;
                    for(int j = 0;j < n;j ++) {
                        if(!(S>>j&1)) { // j is alive, make it dead
                            if(isok(i+1,S^(1<<j))) {
                                double k = C[i-sum[S]][a[j+1]-1] / nt;
                                f[i+1][S^(1<<j)] += f[i][S] * k;
                            }
                        }
                    }
                    if(isok(i+1,S)) {
                        f[i+1][S] += f[i][S] / nt;
                    }
                } else {
                    f[i][S] = 0;
                }
            }
            if(m >= sum[S]) {
                ans += f[m][S] * G[m-sum[S]][S] * t;
            }
        }
        printf("%.10f
    ",(double)ans);
    }
    
    /*
    15 100
    7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
    
    */
    
    
  • 相关阅读:
    python学习笔记1--datetime的使用
    python学习笔记2--子类父类继承时的参数传递
    python学习笔记1--错误,异常,调试
    JS同异步编程
    AMD /CMD
    i++ && ++i
    将url问号后面的参数变成对象
    字符串的常用方法
    函数 && 函数运行机制
    Math数学函数及常用方法
  • 原文地址:https://www.cnblogs.com/dqsssss/p/13882193.html
Copyright © 2011-2022 走看看