區間縮小

onlyblues發表於2024-10-20

區間縮小

題目描述

給定一個正整數 $n$,我們初始設定兩個變數 $l$ 和 $r$,其中 $l=1$,$r=n$。我們將執行以下步驟:

  1. 如果 $l=r$,則結束操作;否則,執行步驟 $2$。
  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

相關文章