洛谷P8208 [THUPC2022 初赛] 骰子旅行 题解 期望DP
题目链接:https://www.luogu.com.cn/problem/P8208
解题思路:
定义 \(d_u\) 表示节点 \(u\) 的出度,定义 \(V_u\) 表示节点 \(u\) 一步能够走到的节点的集合。
定义状态 \(p_{u, c, v}\) 表示从节点 \(u\) 出发走恰好 \(c\) 步的情况下,至少经过一次节点 \(v\) 的概率。
则:
- 若 \(v = u\),则 \(p_{u, c, v} = 1\);
- 否则,若 \(c = 0\),则 \(p_{u, c, v} = 0\);
- 否则,\(p_{u, c, v} = \frac{1}{d_u} \sum\limits_{x \in V_u} p(x, c-1, v)\)
定义 \(f_{u, c}\) 表示从节点 \(u\) 开始走恰好 \(c\) 步,废话指数的期望值,则:
- 若 \(c = 0\),则 \(f_{u, 0} = 0\);
- 否则,\(f(u, c) = \frac{1}{d_u} \sum\limits_{v \in V_u} f(v, c-1) + v \times p(v, c-1, u)\)
注:这道题目的意思有点绕 如果是从 \(u\) 先走到 \(v\),然后再绕回 \(u\),则额外增加的代价是 \(v\),而不是 \(u\)。所以会看见,上式中额外增加的代价是 \(v\) 而不是 \(u\)。即标下划线的部分:
\[f(u, c) = \frac{1}{d_u} \sum\limits_{v \in V_u} f(v, c-1) + \underline{v} \times p(v, c-1, u)
\]
这里是 \(v\) 不是 \(u\)。
示例程序:
#include <bits/stdc++.h>
using namespace std;
const long long mod = 998244353;
typedef long long ll;
void gcd(ll a , ll b , ll &d , ll &x , ll &y) {
if(!b) {d = a; x = 1; y = 0;}
else { gcd(b , a%b,d,y , x); y -= x * (a/b); }
}
ll inv(ll a , ll n = mod) {
ll d , x , y;
gcd(a , n , d, x , y);
return d == 1 ? (x+n)%n : -1;
}
ll p[105][105][105], f[105][105], d[105];
bool visp[105][105][105], visf[105][105];
vector<int> g[105];
int n, s, T;
ll dfsp(int u, int c, int v) {
if (v == u) return 1;
if (c == 0) return 0;
if (visp[u][c][v])
return p[u][c][v];
visp[u][c][v] = true;
ll res = 0;
for (auto x : g[u])
res += dfsp(x, c-1, v),
res %= mod;
res = res * inv(d[u]) % mod;
return p[u][c][v] = res;
}
ll dfsf(int u, int c) {
if (c == 0) return 0;
if (visf[u][c])
return f[u][c];
visf[u][c] = true;
ll res = 0;
for (auto v : g[u])
res += dfsf(v, c-1) + v * dfsp(v, c-1, u) % mod,
res %= mod;
res = res * inv(d[u]) % mod;
return f[u][c] = res;
}
int main() {
cin >> n >> s >> T;
for (int u = 1; u <= n; u++) {
cin >> d[u];
for (int j = 0; j < d[u]; j++) {
int v;
cin >> v;
g[u].push_back(v);
}
}
cout << dfsf(s, T) << endl;
return 0;
}