Page Top

AT_abc323_e Playlist 题解

AT_abc323_e Playlist 题解

一道概率论和动态规划的题,这里的题解主要是翻译原文和个人理解。

问题陈述

有一个包含 \(n\) 首歌曲的播放列表。歌曲 \(i\space(1 \leq i \le n)\) 的长度为整数 \(t_i\) 秒。从第 \(0\) 秒开始随机播放该播放列表。

随机播放重复以下步骤:从 \(n\) 首歌曲中以相等的概率选择一首歌曲,将该歌曲播放完。一首歌播放完,另一首歌就会立即开始播放。同一首歌可以连续选择。

求歌曲 \(1\)\((x + 0.5)\) 秒正在播放的概率(即第 \(1\) 首歌正在被播放),答案对 \(998244353\) 取模。

如何取模

可以证明,本题中要求得的概率总是一个有理数。另外,这个问题的约束条件保证了当要求得的概率用既约分数 \(\frac{y}{x}\) 表示时,\(x\) 不能被 \(998244353\) 整除。

那么,在 \(0\)\(998244352\)(含)之间有一个唯一的整数 \(z\),使得 \(xz \equiv y \pmod{998244353}\),请输出 \(z\)

题目分析

\(f(i)\) 为在第 \(i\) 秒切换歌曲的概率,\(i\in[0,x]\),对 \(998244353\) 取模。

事件「歌曲 \(1\)\(x+0.5\) 秒播放」,与事件「该歌曲在 \(x,(x-1),\ldots,(x-t_1+1)\) 秒开始播放」是互斥的,即这些事件只可能有一个发生。

因此,只需求得歌曲 \(1\) 在时间 \(i\in\{x,(x-1),\ldots,(x-t_1+1)\}\) 的概率之和即可。

因为歌曲的长度一定为整数,因此如果歌曲 \(1\) 在其中某一个时间 \(i\) 播放,那么这首歌曲一定在 \(x+0.5\) 秒正在播放。

因为选择歌曲的概率相等,因此歌曲 \(1\) 在时间 \(i\) 开始播放的概率为任意歌曲在时间 \(i\) 开始播放的概率的 \(\frac{1}{n}\)

因此,\(\text{ans}=\frac{1}{n}\sum_{j=\max\{x-t_1+1,0\}}^{x}f_j\)

因此,只需找到每个 \(f(i)\) 即可,可以通过动态规划计算。

显然的,\(f(0)=1\)\(f(i)=\frac{1}{n}\sum_{j=1}^{n}f(i-t_j)\)

可以这么理解,对于 \(i\ge1\),我们考虑哪首歌曲在 \(i\) 时刻结束,这样 \(p(i)\) 就可以表示为:歌曲 \(j\in[1,n]\)\((i-t_j)\) 时刻开始的概率。正如我们已经讨论过的,\(i-t_j<0\Rightarrow0\)\(i-t_j\ge0\Rightarrow\frac{1}{n}\times f(i-t_j)\)。因此计算 \(f(i)=\frac{1}{n}(f[i-t_1]+f[i-t_2]+\cdots+f[i-t_n])\) 就足够了。

计算每个 \(f(i)\) 需要花费 \(\mathcal{O}(n)\) 时间,计算所有 \(i\in[1,x]\) 的总时间复杂度是 \(\mathcal{O}(nx)\);然后,计算答案需要花费 \(\mathcal{O}(\min\{t_1,x\})\)时间。因此,总体复杂度为 \(\mathcal{O}(nx)\),足以快速解决问题。

注意除法取模需要用到模意义下的乘法逆元,详见我的文章:https://www.cnblogs.com/RainPPR/p/linear-congruence-equation-and-inverse.html

示例代码

#include <bits/stdc++.h>

using namespace std;

using ll = long long;

const ll MOD = 998244353;

#define rr(x) read<x>()
template<typename _tp>
inline _tp read() {
    _tp num = 0, flag = 1;
    char ch = getchar();
    for (; !isdigit(ch); ch = getchar()) if (ch == '-') flag = -1;
    for (; isdigit(ch); ch = getchar()) num = num * 10 + ch - '0';
    return num * flag;
}

ll qpow(ll a, ll b, ll mod) {
    ll res = 1;
    for (; b; b >>= 1, a = a * a % mod) if (b & 1) res = res * a % mod;
    return res;
}

int t[1010];
ll f[20010];

int main() {
    int n = rr(int), x = rr(int);
    int vn = qpow(n, MOD - 2, MOD);
    for (int i = 1; i <= n; ++i) t[i] = rr(int);
    f[0] = 1; for (int i = 1; i <= x; ++i) {
        for (int j = 1; j <= n; ++j) if (i >= t[j]) f[i] = (f[i] + f[i - t[j]]) % MOD;
        f[i] = f[i] * vn % MOD;
    } ll res = 0; for (int i = max(0, x - t[1] + 1); i <= x; ++i) {
        res = (res + f[i]) % MOD;
    } res = res * vn % MOD;
    printf("%lld\n", res);
    return 0;
}
posted @ 2023-10-19 14:20  RainPPR  阅读(9)  评论(0编辑  收藏  举报