區間縮小
題目描述
給定一個正整數 $n$,我們初始設定兩個變數 $l$ 和 $r$,其中 $l=1$,$r=n$。我們將執行以下步驟:
- 如果 $l=r$,則結束操作;否則,執行步驟 $2$。
- 從區間 $[l,r]$ 中等機率地選取一個正整數 $x$。然後,以下兩種情況互斥地發生:以機率 $p$ 將 $l$ 更新為 $x$,以機率 $1−p$ 將 $r$ 更新為 $x$。接著返回步驟 $1$。
經過上述操作,最終必然會有 $l=r$。設隨機變數 $X$ 為最終得到的數字(即 $l$),求 $X$ 的數學期望。 答案對 $998244353$ 取模。
輸入描述:
第一行給出兩個正整數 $n,x$ $(1 \leq n \leq 10^6 ,0 \leq x \leq 100)$,其中 $n$ 的具體意義如題意所示。令 $p= \frac{100}{x}$ 。其中 $p$ 的意義如題意所示。
輸出描述:
輸出一個整數,表示答案。
示例1
輸入
234 10
輸出
898419942
解題思路
期望題就沒一道會做的。
最關鍵的性質,看不出來就別想做出來了。定義 $E(l,r)$ 表示在區間 $[l,r]$ 內最終得到的數字的期望值,有 $E(l+x,r+x) = E(l,r) + x$。
簡單證明。定義 $p_i \, (l \leq i \leq r)$ 表示最終得到的數字為 $i$ 的機率,那麼有 $E(l,r) = \sum\limits_{i=l}^{r}{i \cdot p_i}$。而 $E(l+x,r+x) = \sum\limits_{i=l}^{r}{(i+x) \cdot p_{i+x}} = \sum\limits_{i=l}^{r}{i \cdot p_{i+x}} + x \sum\limits_{i=l}^{r}{p_{i+x}} = \sum\limits_{i=l}^{r}{i \cdot p_{i+x}} + x$。又因為區間 $[l,r]$ 與 $[l+x,r+x]$ 的長度一樣,依據題意長度相同的區間最終得到數字的相對位置的機率相同,即 $p_i = p_{i+x}$,因此有 $E(l+x,r+x) = E(l,r)+x$。
所以對於每種長度的區間,我們只需求出 $E(1,i) \, (1 \leq i \leq n)$ 即可。用 dp 的思路求解該狀態,即根據下一步左端點或右端點停留在哪個位置進行狀態轉移,有狀態轉移方程
$$
\begin{align*}
&E(1,i) = \sum\limits_{j=1}^{i}{\frac{p \cdot E(j,i)}{i}} + \sum\limits_{j=1}^{i}{\frac{(1-p) \cdot E(1,j)}{i}} \\
\Rightarrow \frac{i-1}{i} \cdot &E(1,i) = \sum\limits_{j=2}^{i}{\frac{p \cdot E(j,i)}{i}} + \sum\limits_{j=1}^{i-1}{\frac{(1-p) \cdot E(1,j)}{i}} \\
&E(1,i) = \frac{1}{i-1} \left({\sum\limits_{j=2}^{i}{p \cdot E(j,i)} + \sum\limits_{j=1}^{i-1}{(1-p) \cdot E(1,j)}}\right) \\
&E(1,i) = \frac{1}{i-1} \left({\sum\limits_{j=1}^{i-1}{p \cdot (E(1,i-j)+j)} + \sum\limits_{j=1}^{i-1}{(1-p) \cdot E(1,j)}}\right) \\
&E(1,i) = \frac{1}{i-1} \left(p \cdot \frac{(i-1) \cdot i}{2} + {\sum\limits_{j=1}^{i-1}{E(1,j)}}\right) \\
\end{align*}
$$
只需累加字首 $E(1,1) + \cdots + E(1,i-1)$ 就可以實現 $O(1)$ 轉移(需要預處理逆元)。
AC 程式碼如下,時間複雜度為 $O(n \log{\text{mod}})$:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 1e6 + 5, mod = 998244353;
int f[N];
int qmi(int a, int k) {
int ret = 1;
while (k) {
if (k & 1) ret = 1ll * ret * a % mod;
a = 1ll * a * a % mod;
k >>= 1;
}
return ret;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, m;
cin >> n >> m;
m = 1ll * m * qmi(100, mod - 2) % mod;
f[1] = 1;
for (int i = 2, s = 1; i <= n; i++) {
f[i] = (s + i * (i - 1ll) % mod * qmi(2, mod - 2) % mod * m) % mod * qmi(i - 1, mod - 2) % mod;
s = (s + f[i]) % mod;
}
cout << f[n];
return 0;
}
參考資料
集美大學第十一屆校程式設計競賽 驗題人題解:https://zhuanlan.zhihu.com/p/1431495113