【训练题43:概率dp】Sample Game | 2021牛客暑期多校训练营4

题意

  • Sample Game | 2021牛客暑期多校训练营4
    n n n 个数字,分别为 1 ∼ n 1\sim n 1n
    选到数字 i i i 的概率为 p i p_i pi
  • 每一轮,你都随机选一个数字 x x x,然后把它放在自己的集合 S S S
    x = max ⁡ { S } x=\max\{S\} x=max{S},则你可以继续选数字
    否则,你获得分数 ∣ S ∣ 2 |S|^2 S2
  • 问你获得分数的期望
    1 ≤ n ≤ 100 1\le n\le 100 1n100

思路

  • 期望倒着推,别想着正着推拉!
    期望的设法一般都很单纯(我遇到的题目中)
    d p [ i ] dp[i] dp[i] 表示开始选到数字 i i i,最后的分数?
    N o ! \color{red}{No!} No!
  • 注意,这里的分数为 ∣ S ∣ 2 |S|^2 S2
    我们有 e ( x 2 ) ≠ e 2 ( x ) e(x^2)\ne e^2(x) e(x2)=e2(x),所以我们的设法不能直接这么设!(这个也是很套路的)
  • d p [ i ] dp[i] dp[i] 表示目前选到数字 i i i,最后的集合大小
    d p 2 [ i ] dp2[i] dp2[i] 表示目前选到数字 i i i,最后的集合大小的平方,也就是分数
    开始转移!
    注意转移也是很套路的。等号后面跟着的就是概率 * (下一个状态 + 贡献)
    d p [ i ] = ∑ j = i n p j × ( d p [ j ] + 1 ) + ∑ j = 1 i − 1 p j × 1 d p 2 [ i ] = ∑ j = i n p j × ( d p 2 [ j ] + 2 d p [ j ] + 1 ) + ∑ j = 1 i − 1 p j × 1 2 dp[i]=\sum_{j=i}^n p_j\times (dp[j]+1)+\sum_{j=1}^{i-1}p_j\times 1\\ dp2[i]=\sum_{j=i}^n p_j\times (dp2[j]+2dp[j]+1)+\sum_{j=1}^{i-1}p_j\times 1^2 dp[i]=j=inpj×(dp[j]+1)+j=1i1pj×1dp2[i]=j=inpj×(dp2[j]+2dp[j]+1)+j=1i1pj×12
    这里又有很多个问题了:
  • 对于 d p [ i ] dp[i] dp[i] 的转移:
    (1)下一步有哪些?下一步选的数字为 j j j,要么是还能继续选 ( j ≥ i j\ge i ji) ,那么期望就是 p j ( d p [ j ] + 1 ) p_j(dp[j]+1) pj(dp[j]+1),这里 + 1 +1 +1 是因为你多选了一个数字,集合大小就 + 1 +1 +1了; 要么是不能选了 ( j < i j < i j<i ),期望就是 p j × 1 p_j\times1 pj×1
    (2)等式两边自己包含自己?把右边关于 d p [ i ] dp[i] dp[i] 的系数移到等式左边,然后就能变成 d p [ i ] = S o m e t h i n g 1 − p i dp[i]=\frac{Something}{1-p_i} dp[i]=1piSomething
  • 对于 d p 2 [ i ] dp2[i] dp2[i] 的转移:
    (1)下一步的贡献怎么算?容易得到 e ( ( x + 1 ) 2 ) = e ( x 2 ) + e ( 2 x ) + e ( 1 ) = e ( x 2 ) + 2 e ( x ) + 1 e((x+1)^2)=e(x^2)+e(2x)+e(1)=e(x^2)+2e(x)+1 e((x+1)2)=e(x2)+e(2x)+e(1)=e(x2)+2e(x)+1
    所以中间展开就是这样了,因为我们根据定义,是从 e ( x 2 ) 推 到 e ( ( x + 1 ) 2 ) e(x^2) 推到 e((x+1)^2) e(x2)e((x+1)2)
    e ( x 2 ) ≠ e 2 ( x ) e(x^2)\ne e^2(x) e(x2)=e2(x)
    e ( x 2 ) ≠ e 2 ( x ) e(x^2)\ne e^2(x) e(x2)=e2(x)
    e ( x 2 ) ≠ e 2 ( x ) e(x^2)\ne e^2(x) e(x2)=e2(x)
    嗯,好多了…
    (2)最后面的是 p j × 1 2 p_j\times 1^2 pj×12,我觉得是这样的,因为下一步无法操作了,贡献就是 1 2 1^2 12
    (3)我们发现 我 们 算 出 来 的 根 本 就 不 是 集 合 大 小 , 而 是 操 作 次 数 呀 ! \color{red}{我们算出来的根本就不是集合大小,而是操作次数呀!}
    最后让你算 e ( i 2 ) e(i^2) e(i2),但是相当于我们算的是 e ( ( i − 1 ) 2 ) e((i-1)^2) e((i1)2)
    嗯?你不会写成 答案 = d p 2 [ i ] + 1 dp2[i]+1 dp2[i]+1 吧 (…)
    答案是 d p 2 [ i ] + 2 d p [ i ] + 1 \color{red}{dp2[i]+2dp[i]+1} dp2[i]+2dp[i]+1

代码

  • 时间复杂度: O ( n 2 log ⁡ n ) O(n^2\log n) O(n2logn),其实可以优化成 O ( n ) O(n) O(n),无所谓!
#include <bits/stdc++.h>
#define IOS ios::sync_with_stdio(false);cin.tie(NULL);cout.tie(NULL);
using namespace std;
typedef long long ll;
void show(){std::cerr << endl;}template<typename T,typename... Args>void show(T x,Args... args){std::cerr << "[ " << x <<  " ] , ";show(args...);}

const int MAX = 150;
const int MOD = 998244353;
const int INF = 0x3f3f3f3f;
const ll LINF = 0x3f3f3f3f3f3f3f3f;
const double EPS = 1e-5;

ll qpow(ll a,ll n){/* */ll res = 1LL;while(n){if(n&1)res=res*a%MOD;a=a*a%MOD;n>>=1;}return res;}
ll qpow(ll a,ll n,ll p){a%=p;ll res = 1LL;while(n){if(n&1)res=res*a%p;a=a*a%p;n>>=1;}return res;}
ll npow(ll a,ll n){/* */ll res = 1LL;while(n){if(n&1)res=res*a;a=a*a;n>>=1;if(res<0||a<0)return 0;}return res;}
ll inv(ll a){/* */return qpow(a,MOD-2);}
ll inv(ll a,ll p){return qpow(a,p-2,p);}

ll dp[MAX],dp2[MAX];
ll p[MAX],w[MAX];
int main()
{
    IOS;
    int n;cin >> n;
    ll sum = 0;
    for(int i = 1;i <= n;++i){
        cin >> w[i];
        sum += w[i];
    }
    for(int i = 1;i <= n;++i){
        p[i] = w[i] * inv(sum) % MOD;
    }
    ll ans = 0;
    for(int i = n;i >= 1;--i){
        ll fz = p[i];
        for(int j = i + 1;j <= n;++j){
            fz = (fz + p[j] * (dp[j] + 1)% MOD) % MOD;
        }
        for(int j = 1;j < i;++j){
            fz = (fz + p[j] * (1) ) % MOD;
        }
        ll fm = (1 - p[i] + MOD) % MOD;
        dp[i] = fz * inv(fm) % MOD;
    }

    for(int i = n;i >= 1;--i){
        ll fz = (p[i] * 2 % MOD * dp[i] % MOD + p[i]) % MOD;
        for(int j = i + 1;j <= n;++j){
            fz = (fz + p[j] * (dp2[j] + 2LL * dp[j] % MOD + 1)% MOD) % MOD;
        }
        for(int j = 1;j < i;++j){
            fz = (fz + p[j] * (1) % MOD) % MOD;
        }
        ll fm = (1 - p[i] + MOD) % MOD;
        dp2[i] = fz * inv(fm) % MOD;
        ans = (ans + (dp2[i] + 2 * dp[i] + 1) % MOD * p[i] % MOD) % MOD;
    }

    cout << ans << endl;
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值