• AGC056E Cheese【概率期望,dp】


    给定长为 \(n\) 的圆周,定义沿顺时针方向距离为 \(x\) 的位置的坐标为 \(x\)。初始时坐标 \(i+0.5\) 的位置上有一只老鼠。

    进行 \(n-1\) 次操作,每次操作以 \(a_i\%\) 的概率选择 \(i\pod{0\le i<n}\),在坐标 \(i\) 放上一块奶酪,之后奶酪顺时针移动,每次遇到没吃过奶酪的老鼠,都有 \(1/2\) 的概率被吃掉。

    \(\forall k\in[0,n)\),求最后没吃奶酪的老鼠是坐标位于 \(k+0.5\) 的老鼠的概率 \(\bmod 998244353\)

    \(n\le 40\)\(a_i\ge 0\)\(\sum a_i=100\)


    只考虑 \(k=n-1\) 的情况,分别做即可。

    本题最关键的结论是,在确定了每个坐标 \(i\) 被放奶酪的次数 \(c_i\) 之后,概率仅与奶酪经过 \(-0.1\) 的次数 \(x\) 有关,而与放奶酪的顺序无关。

    \(b_i\) 表示奶酪经过 \(i+0.1\) 的次数,则 \(b_i=x-i+\sum_{j=0}^ic_j\),而整个过程实际上是一个奶酪与老鼠的匹配,所以与放奶酪的顺序无关,乘上个可重排列方案数即可,而老鼠决策的概率为 \(2^{-x}\prod_{i=0}^{n-2}(1-2^{-b_i})\),表示第 \(0,1,\cdots,n-2\) 个老鼠至少要吃掉一个奶酪(虽然实际上只有第一次是吃了的),而第 \(n-1\) 个老鼠不能吃奶酪。

    而如果任意取 \(c_i\)\(x\)\(b_i\) 有可能是负的,但因为 \(b_{i+1}-b_i\ge -1\) 所以此时必有一个 \(b_i=0\),算出的概率即为 \(0\),因此不需考虑这种情况。

    最后答案即为 \((n-1)!\sum2^{-x}\prod_{i=0}^{n-2}(1-2^{i-\sum_{j\le i}c_j}\cdot 2^{-x})\prod_{i=0}^{n-1}\frac{a_i^{c_i}}{c_i!}\),要对所有 \(x\ge 0\) 求和所以应该表示为 \(2^{-x}\) 的多项式,对 \(p\) 次项系数乘上 \(1/(1-2^{-p})\) 然后加起来即可。直接 dp 维护,单次计算时间复杂度 \(O(n^4)\)

    然而你会发现你输出的恰好是答案的 \(2\) 倍,因为对于任意一个合法方案,把它加上一个独立的圈之后的概率也被算了进去,而加一个圈之后概率变为原来的 \(1/2\)(不能被第 \(n-1\) 个老鼠吃掉),所以算出来的是答案的 \(1+1/2+1/4+\cdots=2\) 倍,最后除以 \(2\) 即可。

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    const int N = 42, mod = 998244353, iv100 = 828542813;
    int ksm(int a, int b){
        if(b < 0) b += mod - 1;
        int res = 1;
        for(;b;b >>= 1, a = (LL)a * a % mod)
            if(b & 1) res = (LL)res * a % mod;
        return res;
    }
    void qmo(int &x){x += x >> 31 & mod;}
    int n, pr[N], a[N], fac[N], inv[N], f[N][N], g[N][N], val[N];
    void solve(){
        memset(f, 0, sizeof f); **f = *val = 1;
        for(int i = 0;i < n;++ i){
            for(int j = 1;j < n;++ j) val[j] = (LL)val[j-1] * inv[j] % mod * a[i] % mod;
            memset(g, 0, sizeof g);
            for(int j = 0;j < n;++ j)
                for(int k = 0;j + k < n;++ k)
                    for(int l = 0;l <= i;++ l)
                        g[j+k][l] = (g[j+k][l] + (LL)f[j][l] * val[k]) % mod;
            memset(f, 0, sizeof f);
            for(int j = 0;j < n;++ j){
                int coe = ksm(2, i - j);
                if(i == n-1) for(int k = 0;k < n;++ k) f[j][k+1] = (LL)coe * g[j][k] % mod;
                else for(int k = 0;k <= i;++ k){qmo(f[j][k] += g[j][k] - mod); f[j][k+1] = mod - (LL)coe * g[j][k] % mod;}
            }
        }
        int res = 0;
        for(int i = 1, pw = 1;i <= n;++ i){
            res = (res + (LL)pw * f[n-1][i] % mod * ksm(2*pw-1, mod-2)) % mod;
            qmo(pw += pw - mod);
        }
        printf("%lld ", (LL)res * fac[n-1] % mod);
    }
    int main(){
        ios::sync_with_stdio(false);
        cin >> n; *fac = fac[1] = inv[1] = 1;
        for(int i = 2;i <= n;++ i){
            fac[i] = (LL)fac[i-1] * i % mod;
            inv[i] = mod - (LL)mod / i * inv[mod % i] % mod;
        }
        for(int i = 0;i < n;++ i){cin >> pr[i]; pr[i] = (LL)pr[i] * iv100 % mod;}
        for(int i = 0;i < n;++ i){
            for(int j = 0;j <= i;++ j) a[n+j-i-1] = pr[j];
            for(int j = i+1;j < n;++ j) a[j-i-1] = pr[j];
            solve();
        }
    }
    
  • 相关阅读:
    安装nginx
    查看Linux内核
    Python学习总结11:获取当前运行类名和函数名
    Python学习总结10:获取shell输出结果
    Python学习总结9:目录操作方法汇总
    Python学习总结8:文件模式及操作方法汇总
    Python学习总结7:随机字符串和随机数
    Python学习总结6:字符串格式化操作及方法总结
    Python学习总结5:数据类型及转换
    Python学习总结4:字符串常量与操作汇总
  • 原文地址:https://www.cnblogs.com/AThousandMoons/p/15725267.html
Copyright © 2020-2023  润新知