[luoguP2973]Driving Out the Piggies G
题意
一个无向图,节点 \(1\) 有一个炸弹,在每个单位时间内,有 \(\dfrac{p}{q}\) 的概率在这个节点炸掉,有 \(1-\dfrac{p}{q}\) 的概率随机选择一条出去的路到其他的节点上,问最终炸弹在每个节点上爆炸的概率。
sol
由于最终炸弹在爆炸后就无法再移动了,因此炸弹到达每个点的次数的期望 \(E \cdot \dfrac{p}{q}\) 即为爆炸的概率。
因此我们可以列出方程
\[f_u=[u=1] + \sum_{v\in son(u)}f_v \cdot (1-\dfrac{p}{q}) \cdot \dfrac{1}{d_v}
\]
(\(d_v\) 为 \(v\) 的度)
这样,我们只需要高斯消元,解出线性方程组即可。
代码
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int N = 305, M = 90005;
const double eps = 1e-8;
int h[N], e[M], ne[M], idx;
long long inn[N];
int n, m, p, q;
double f[N], coeff[N][N];
void add(int a, int b){
e[idx] = b, ne[idx] = h[a], h[a] = idx ++ ;
}
void gauss(){
for (int i = 1; i <= n; i ++ ){
int row = 0;
double maxn = -1e9;
for (int j = i; j <= n; j ++ )
if (abs(coeff[i][j]) > maxn) row = j, maxn = abs(coeff[i][j]);
swap(coeff[i], coeff[row]);
double Main = coeff[i][i];
if (abs(Main) <= eps) {
puts("NO SOLUTION");
return;
}
for (int j = i; j <= n + 1; j ++ ) coeff[i][j] /= Main;
for (int j = 1; j <= n; j ++ ){
if (j == i) continue;
double div = coeff[j][i];
for (int k = i; k <= n + 1; k ++ ) coeff[j][k] -= div * coeff[i][k];
}
}
}
int main(){
memset(h, -1, sizeof h);
scanf("%d%d%d%d", &n, &m, &p, &q);
while (m -- ){
int u, v;
scanf("%d%d", &u, &v);
add(u, v), add(v, u);
inn[u] ++ , inn[v] ++ ;
}
for (int u = 1; u <= n; u ++ ){
if (u == 1) coeff[u][n + 1] = -1;
coeff[u][u] = -1;
for (int i = h[u]; ~i; i = ne[i]){
int j = e[i];
coeff[u][j] = 1.0 * (q - p) / (q * inn[j]);
}
}
gauss();
for (int i = 1; i <= n; i ++ ) printf("%.9lf\n", coeff[i][n + 1] * p / q);
return 0;
}