难度
提 高 + / 省 选 − \color{blue} 提高+/省选- 提高+/省选−
题意
给定一个 DAG, N N N 个节点, M M M 条单向边。
每条边有一个移动概率和长度(边权)。
问你从第一个点出发,到最后终点之后,移动的 距离的期望 是多少?
给定了一些特殊的降低题目难度的要求,可以略。
思路
w a , b w_{a,b} wa,b 表示两个节点之间的边权。
我们设 f x f_x fx 表示起点到节点 x x x 的概率。
我们设 f x → v f_{x\rightarrow v} fx→v 表示节点 x x x 到节点 v v v 的移动概率。
我们设 e x e_x ex 表示起点到节点 x x x 的距离期望。
我们设 e x ′ e^\prime_x ex′ 表示从节点 x x x 到终点 n n n 的距离期望。
-
【逆向推】
对于从节点 v v v 到 v v v 的后继节点集合 x ∈ S x\in S x∈S ,我们有:
e v ′ = ∑ x ∈ S ( e x ′ + w v , x ) f v → x e^\prime_v=\underset{x\in S}{\sum}(e^\prime_x+w_{v,x})f_{v\rightarrow x} ev′=x∈S∑(ex′+wv,x)fv→x
注意:逆向推需要图建反边,并且需保证起点能经过其他所有中间节点。 -
【正向推】
对于从节点 x x x 到节点 x x x 的后继节点集合 v ∈ S v\in S v∈S ,我们有:
f v = ∑ x ∈ S f x × f x → v f_v=\underset{x\in S}{\sum} f_x\times f_{x\rightarrow v} fv=x∈S∑fx×fx→v
e v = ∑ x ∈ S ( e x + f x × w x , v ) f x → v e_v=\underset{x\in S}{\sum}(e_x+f_x\times w_{x,v})f_{x\rightarrow v} ev=x∈S∑(ex+fx×wx,v)fx→v
最推荐的写法,这样可以求出起点到所有中间节点的期望了
注意一下边界条件 f 1 = 1 f_1=1 f1=1 -
【正向推2】
对于从节点 x x x 到节点 x x x 的后继节点集合 v ∈ S v\in S v∈S ,我们有:
f v = ∑ x ∈ S f x × f x → v f_v=\underset{x\in S}{\sum} f_x\times f_{x\rightarrow v} fv=x∈S∑fx×fx→v
e 终 点 = ∑ e d g e w ( f w a × f w a → w b × w a , b ) e_{终点}=\underset{edge\ w}{\sum}(f_{w_a}\times f_{w_a\rightarrow w_b}\times w_{a,b}) e终点=edge w∑(fwa×fwa→wb×wa,b)
很好理解,就是算出每条边对终点答案的贡献。
注意:需要保证每个中间节点都能走到终点。
注意一下边界条件 f 1 = 1 f_1=1 f1=1 -
然后需要注意的是,我们递推需要使用拓扑序计算即可。
核心代码
正向推写法:
时间复杂度: O ( N + M ) O(N+M) O(N+M)
/*
_ __ __ _ _
| | \ \ / / | | (_)
| |__ _ _ \ V /__ _ _ __ | | ___ _
| '_ \| | | | \ // _` | '_ \| | / _ \ |
| |_) | |_| | | | (_| | | | | |___| __/ |
|_.__/ \__, | \_/\__,_|_| |_\_____/\___|_|
__/ |
|___/
*/
const int MAX = 1e5+50;
vector<pair<int,int> >V[MAX];
int in[MAX];
double f[MAX];
double e[MAX];
queue<int>Q;
int main()
{
int n,m;
scanf("%d%d",&n,&m);
while(m--){
int ta,tb,tc;
scanf("%d%d%d",&ta,&tb,&tc);
V[ta].push_back(make_pair(tb,tc));
in[tb]++;
}
for(int i = 1;i <= n;++i)if(!in[i])Q.push(i);
f[1] = 1;
while(!Q.empty()){
int x = Q.front();
Q.pop();
int sz = V[x].size();
for(auto it : V[x]){
int v = it.first;
int w = it.second;
double pro = 1.0 / sz;
f[v] += f[x] * pro;
e[v] += pro * w * f[x];
e[v] += pro * e[x];
in[v]--;
if(!in[v])Q.push(v);
}
}
printf("%.2lf",e[n]);
return 0;
}