luogu P6835 概率DP 期望
洛谷 P6835
題意
n + 1個節點,第i個節點都有指向i + 1的一條單向路,現在給他們新增m條邊,每條邊都從一個節點指向小於等於自己的一個節點,現在從1號點開始走,每次等概率地選擇出邊,問到達n+1的步數期望
思路
-
用 \(F_{i,j}\) 代表從i到j的期望步數
-
由於期望的線性性質,所以 \(F_{i,k} + F_{k,j} = F_{i,j}\) 所以我們算出每個 \(F_{i,i+1}\) 最後輸出字首和sum[n]即可
-
對於當前節點i,出度為 \(d_i\), 所以選某條邊的概率為
\[\frac{1}{d_i}
\]
- 選直接連線i+1的那條邊,步數為1,即期望步數為1 / d,而選擇其他邊的期望步數為
\[\frac{\sum_{j\in v[i]}{(1 + F_{j,i} + F_{i,i+1})}}{d_i}
\]
- 上面式子像是一個“遞迴式”,左右都有我們希望計算的\(F_{i,i+1}\),整理如下:
\[F_{i,i+1} = \frac{1}{d_i} + \frac{\sum_{j\in v[i]}{(1 + F_{j,i} + F_{i,i+1})}}{d_i}
\]
即
\[d_i * F_{i,i+1} = 1 + \sum_{j\in v[i]}{(1 + F_{j,i} + F_{i,i+1})}
\]
即
\[d_i * F_{i,i+1} = 1 + d_i - 1 + (d_i - 1) * F_{i,i+1} + \sum_{j\in v[i]}{F_{j,i}}
\]
即
\[F_{i,i+1} = d_i + \sum_{j\in v[i]}{F_{j,i}}
\]
其中 \(F_{j,i}\) 可以字首和得到,最終複雜度為線性
注意:
- 字首和取模處理難免有後面的值小於前面的時候,所以每次相減時要加一個mod防止變為負數
AC程式碼
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <vector>
#include <cmath>
using namespace std;
const long long modd = 998244353;
int n, m;
long long ff[1000005] = {0};
vector<int> vv[1000005];
long long su[1000005] = {0};
int main()
{
int id;
scanf("%d%d%d", &id, &n, &m);
for (int i = 1; i <= m; ++i)
{
int xx, yy;
scanf("%d%d", &xx, &yy);
vv[xx].push_back(yy);
}
for (int i = 1; i <= n; ++i)
{
long long d = vv[i].size();
ff[i] = d + 1;
for (unsigned int j = 0; j < d; ++j)
{
ff[i] = (ff[i] % modd + (su[i - 1] - su[vv[i][j] - 1] + modd) % modd) % modd;
}
su[i] = (su[i - 1] % modd + ff[i] % modd) % modd;
}
printf("%lld\n", su[n]);
return 0;
}