链接:https://ac.nowcoder.com/acm/contest/11255/B

这篇题解可能会有些许错误,欢迎大佬们指正Orz

概率生成函数

首先,我们定义概率生成函数
(为啥这么定义?菜了,不大清楚,当一个做题方向吧)
我们现在需要的出这个函数。

由题意可知:我们抽到的数字的序列,除去最后一个数之外,它肯定是一个不下降的序列,像是1,1,2,3,3,7这样的。这时,我们可以枚举每个数的出现次数,来得到指的是数字i被抽到的概率)
具体的,假如1,1,2,3,3,7这样的序列,对应的就是。我们某个数出现的次数的生成函数,则我们可以知道
根据等比数列求和,
因此,
记住这个函数,我们后面会用到它。
PS.枚举了1,1,3,3,7这种序列后游戏并没有结束,因此它是P(len>i)的概率。

回到题目,我们要求的是的期望,那么,用上面的东西表示其实是:

这里有一些细节可能需要学过生成函数才能比较好理解,这里不再展开。
ps.官方题解这里第一步有些许不同,但是推到结果相同,不知道是我理解有错还是出题人笔误?欢迎大佬们指出。

upd.这里有大佬说推导会漏掉这一项,但目前还没想出问题所在Orz。

upd 2021.7.21. 修改了第4行处一处错误,感谢各位大佬的指正Orz。

注意:以后推累加的式子最好都把前几项写出来看下。
之后就是怎么求这个式子的问题了。
首先是怎么求,这个好解决,
之后,根据,x=1带入也可以得到f(1)。
而对于,我们这样考虑:
对于形如函数,其导数为
因此,对于,g(x)形如h(x)
其导数为

因此,
这样就求和,后面求和直接计算出来,乘上f(1)即可。

#include <bits/stdc++.h>
#include <iostream>
#include <ctime>
#include <cstdio>
#include <cctype>
#define inf 0x3f3f3f3f
#define IOS                       \
    ios_base::sync_with_stdio(0); \
    cin.tie(0);
#define rep(i, a, n) for (int i = a; i <= n; ++i)
#define per(i, a, n) for (int i = n; i >= a; --i)
//#define ONLINE_JUDGE
using namespace std;
typedef long long ll;
const int mod = 998244353;
namespace FastIO
{
    char buf[1 << 21], buf2[1 << 21], a[20], *p1 = buf, *p2 = buf, hh = '\n';
    int p, p3 = -1;
    void read() {}
    void print() {}
    inline int getc()
    {
        return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++;
    }
    inline void flush()
    {
        fwrite(buf2, 1, p3 + 1, stdout), p3 = -1;
    }
    template <typename T, typename... T2>
    inline void read(T &x, T2 &...oth)
    {
        int f = 0;
        x = 0;
        char ch = getc();
        while (!isdigit(ch))
        {
            if (ch == '-')
                f = 1;
            ch = getc();
        }
        while (isdigit(ch))
        {
            x = x * 10 + ch - 48;
            ch = getc();
        }
        x = f ? -x : x;
        read(oth...);
    }
    template <typename T, typename... T2>
    inline void print(T x, T2... oth)
    {
        if (p3 > 1 << 20)
            flush();
        if (x < 0)
            buf2[++p3] = 45, x = -x;
        do
        {
            a[++p] = x % 10 + 48;
        } while (x /= 10);
        do
        {
            buf2[++p3] = a[p];
        } while (--p);
        print(oth...);
    }
} // namespace FastIO
#define read FastIO::read
#define write FastIO::print
int gcd(int a, int b)
{
    return b == 0 ? a : gcd(b, a % b);
}
int lcm(int a, int b) { return a / gcd(a, b) * b; };
ll ksm(ll a, ll n)
{
    ll ans = 1;
    while (n)
    {
        if (n & 1)
            ans = (ans * a) % mod;
        a = a * a % mod;
        n >>= 1;
    }
    return ans % mod;
}
//==============================================================
const int maxn=105;
#define MOD(x) (((x)%mod+mod)%mod)
ll n,p[maxn];

int main()
{
#ifndef ONLINE_JUDGE
    freopen("in.txt", "r", stdin);
    freopen("out.txt", "w", stdout);
#endif
    //clock_t c1 = clock();
    //===========================================================
    read(n);
    int sum=0;
    rep(i,1,n)read(p[i]),sum=(sum+p[i]);
    for(int i=1;i<=n;++i){
        p[i]=MOD(p[i]*ksm(sum,mod-2));
    }
    ll fval=1;
    for(int i=1;i<=n;++i){
        fval*=ksm(1-p[i],mod-2);
        fval=MOD(fval);
    }
    ll gval=0;
    for(int i=1;i<=n;++i){
        gval+=1ll*p[i]*ksm(1-p[i],mod-2);
        gval=MOD(gval);
    }
    ll f_val=gval*fval;
    cout<<MOD(f_val*2%mod+fval)<<endl;
    //===========================================================
    FastIO::flush();
    //std::cerr << "Time:" << clock() - c1 << "ms" << std::endl;
    return 0;
}

期望dp

看到有大佬用的期望dp。其实比赛的时候也想过,但是最后十分钟发现题意假了,怕了,没想出来

其实期望dp的转移是比较套路的,一般难点在状态设计上。但是这题的状态设计其实又比较明显:
对于的期望,我们一般还是先考虑求的期望,再根据期望的线性性解决。
由于这题的步骤可以有无限步,因此不可能选择步数作为状态。显然,我们选择的会是目前的最大值。
dp[i]表示非递减部分现在最大值为i时,还需要生成的数的个数的期望,则:
dp[i]有两个来源:
1、下一步选到了一个数j>=i,则还需要继续选择,这一步的概率为
2、下一步选到了一个数j<i,则下一步就结束,这一步的概率也为
因此,可以列出dp转移方程:

化简下就是

从n->1推即可。

对于

得:

同理化简得:

同样推即可。
答案为:
或者上面的
因为我们算(i>0)实际是已经选取了一个数后再进行的。

mod=998244353
dp=[i for i in range(105)]
dp2=[i for i in range(105)]
p=[i for i in range(105)]
n=int(input())
w=list(map(int,input().split()))

sum=0
for x in w:
    sum+=x
Inv=pow(sum,mod-2,mod)

for i in range(n):
    p[i]=w[i]*Inv%mod

for i in reversed(range(n)):
    pre=0
    suf=0
    for j in range(i):
        pre+=p[j]
        pre%=mod
    for j in range(i+1,n):
        suf+=p[j]*(dp[j]+1)%mod
        suf%=mod
    inv=pow((1-p[i])%mod,mod-2,mod)
    dp[i]=(pre+suf+p[i])*inv%mod
for i in reversed(range(n)):
    up=0
    for j in range(i+1,n):
        up+=p[j]*(dp2[j]+2*dp[j]+1)%mod
        up%=mod
    for j in range(i):
        up+=p[j]
        up%=mod
    up+=2*p[i]*dp[i]+p[i]
    up%=mod
    down=pow((1-p[i])%mod,mod-2,mod)
    dp2[i]=up*down%mod

print(dp2[0])