题意:
给定一个 n 个点、m 条边的有向无环图,现在要从1号结点沿边走向 n 号结点,每天等概率走向某一邻接结点或者停留在原地,每天花费代价为经历的天数,求到 n 号结点时的期望代价。保证只有 1 号结点无入度,只有 n 号结点无出度。(n <= 1e5, m <= 2e5)
链接:
https://nanti.jisuanke.com/t/41301
题解:
期望dp,f[u] 表示 u 走到 n 号结点的天数期望,g[u] 表示 u 走到 n 号结点的代价期望。记 u -> v,k 为 u 的出度,则 f[u] = f [ u ] + ∑ f [ v ] k + 1 + 1 \cfrac{f[u] + \sum_{}f[v]}{k + 1} + 1 k+1f[u]+∑f[v]+1,移项整理得 f[u] = ∑ f [ v ] + k + 1 k \cfrac{\sum_{}f[v] + k + 1}{k} k∑f[v]+k+1,同理 g[u] = ( g [ u ] + f [ u ] + 1 ) + ∑ ( g [ v ] + f [ v ] + 1 ) k + 1 \cfrac{(g[u] + f[u] + 1) + \sum_{}(g[v] + f[v] + 1)}{k + 1} k+1(g[u]+f[u]+1)+∑(g[v]+f[v]+1),移项整理得 g[u] = f [ u ] + 1 + ∑ ( g [ v ] + f [ v ] + 1 ) k \cfrac{f[u] + 1 + \sum_{}(g[v] + f[v] + 1)}{k} kf[u]+1+∑(g[v]+f[v]+1),原图反建,从 n 号结点拓扑转移即可。
参考代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
#define pb push_back
#define sz(a) ((int)a.size())
#define mem(a, b) memset(a, b, sizeof a)
#define lson (rt << 1)
#define rson (rt << 1 | 1)
#define gmid (l + r >> 1)
const int maxn = 1e5 + 5;
const int maxm = 1e6 + 5;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
vector<int> G[maxn];
double f[maxn], g[maxn];
int in[maxn], deg[maxn];
int n, m;
void solve(){
queue<int> q; q.push(n);
while(!q.empty()){
int u = q.front(); q.pop();
for(int i = 0; i < sz(G[u]); ++i){
int v = G[u][i];
if(--in[v] == 0) q.push(v);
f[v] += (f[u] + (deg[v] + 1.0) / deg[v]) / deg[v];
}
}
for(int i = 1; i <= n; ++i) in[i] = deg[i];
q.push(n);
while(!q.empty()){
int u = q.front(); q.pop();
for(int i = 0; i < sz(G[u]); ++i){
int v = G[u][i];
if(--in[v] == 0) q.push(v);
g[v] += (g[u] + f[u] + 1 + (f[v] + 1) / deg[v]) / deg[v];
}
}
}
int main(){
// ios::sync_with_stdio(0); cin.tie(0);
int t; scanf("%d", &t);
while(t--){
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; ++i) G[i].clear(), f[i] = g[i] = in[i] = deg[i] = 0;
for(int i = 1; i <= m; ++i){
int u, v; scanf("%d%d", &u, &v);
G[v].pb(u); ++in[u], ++deg[u];
}
solve();
printf("%.2lf\n", g[1]);
}
}