link:https://nanti.jisuanke.com/t/41301
思路link:https://blog.csdn.net/weixin_43394931/article/details/100513821
题意
给出一个n个点的DAG,起点保证为1,终点保证为n,在每一个点,每一天有等概率走向其相邻的点或是停在原点。在第i天的损耗值为i,求走到终点的期望损耗值。
思路
定义两个数组
d
[
i
]
,
c
[
i
]
d[i],c[i]
d[i],c[i]分别代表第i个位置到终点的期望天数以及期望损耗值。
可以这么理解:第一天的损耗值为1,第二天为2,……,第x天为x。可以等价为第一天为x,第二天为x-1,……,第x天为1。
这样第
i
i
i个位置的期望损耗值可以由其指向的点
j
j
j更新而得,期望损耗值为
c
[
j
]
+
d
[
j
]
+
1
c[j]+d[j]+1
c[j]+d[j]+1。
可以列出方程:
d
[
i
]
=
∑
d
[
j
]
x
+
1
+
d
[
i
]
x
+
1
+
1
d[i] = \frac{\sum{d[j]}}{x+1}+\frac{d[i]}{x+1}+1
d[i]=x+1∑d[j]+x+1d[i]+1
c
[
i
]
=
∑
c
[
j
]
+
d
[
j
]
+
1
x
+
1
+
c
[
i
]
+
d
[
i
]
+
1
x
+
1
c[i] =\frac{\sum{c[j]+d[j]+1}}{x+1}+\frac{c[i]+d[i]+1}{x+1}
c[i]=x+1∑c[j]+d[j]+1+x+1c[i]+d[i]+1
解得:
d
[
i
]
=
∑
d
[
j
]
x
+
x
+
1
x
d[i] = \frac{\sum{d[j]}}{x}+\frac{x+1}{x}
d[i]=x∑d[j]+xx+1
c
[
i
]
=
∑
c
[
j
]
+
d
[
j
]
+
1
x
+
d
[
i
]
+
1
x
c[i] = \frac{\sum{c[j]+d[j]+1}}{x}+\frac{d[i]+1}{x}
c[i]=x∑c[j]+d[j]+1+xd[i]+1
期望由于要逆推,所以需要先搞出拓扑序,然后从拓扑序反向更新。
代码
//
// Created by yjq on 2019/9/4.
//
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define ld long double
#define ull unsigned long long
#define __ ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)
const int maxn = 5e5 + 10;
struct pxy {
int to, next;
} e[maxn * 2];
int head[maxn], cnt, id[maxn], od[maxn];
void ins(int x, int y) {
e[++cnt].to = y;
e[cnt].next = head[x];
head[x] = cnt;
}
int n, m;
ld d[maxn], c[maxn];
queue<int> q;
vector<int> v;
int main() {
__;
int _;
cin >> _;
while (_--) {
cin >> n >> m;
memset(head, 0, sizeof head);
memset(od, 0, sizeof od);
memset(id, 0, sizeof id);
for (int i = 1; i <= m; ++i) {
int x, y;
cin >> x >> y;
od[x]++;
id[y]++;
ins(x, y);
}
v.clear();
for (int i = 1; i <= n; ++i) {
if (id[i] == 0)q.push(i);
}
while (!q.empty()) {
int x = q.front();
v.push_back(x);
q.pop();
for (int i = head[x]; i; i = e[i].next) {
id[e[i].to]--;
if (id[e[i].to] == 0)q.push(e[i].to);
}
}
memset(d, 0, sizeof d);
memset(c, 0, sizeof c);
d[n] = 0;
c[n] = 0;
for (int i = (int) v.size() - 2; i >= 0; --i) {
ld w = 0;
int x = v[i];
for (int j = head[x]; j; j = e[j].next) {
w += d[e[j].to];
}
d[x] = w / (ld) od[x] + (ld) (od[x] + 1) / (ld) od[x];
}
for (int i = (int) v.size() - 2; i >= 0; --i) {
ld w = 0;
int x = v[i];
for (int j = head[x]; j; j = e[j].next) {
w += d[e[j].to] + c[e[j].to] + 1.0;
}
c[x] = w / (ld) od[x] + (d[x] + 1.0) / (ld) od[x];
}
cout << fixed << setprecision(2) << c[1] << endl;
}
return 0;
}