[Luogu P2973] [BZOJ 1778] [USACO10HOL]赶小猪Driving Out the Piggies

#### 洛谷传送门

BZOJ传送门

题目描述

奶牛们建立了一个随机化的臭气炸弹来驱逐猪猡。猪猡的文明包含 1 1 N ( 2N300 2 ≤ N ≤ 300 )一共 N N 个猪城。这些城市由M ( 1M44,850 1 ≤ M ≤ 44 , 850 )条由两个不同端点 Aj A j Bj B j ( 1AjN 1 ≤ A j ≤ N ; 1BjN 1 ≤ B j ≤ N )表示的双向道路连接。保证城市 1 1 至少连接一个其它的城市。一开始臭气弹会被放在城市1。每个小时(包括第一个小时),它有 PQ P Q ( 1P1,000,000 1 ≤ P ≤ 1 , 000 , 000 ; 1Q1,000,000 1 ≤ Q ≤ 1 , 000 , 000 )的概率污染它所在的城市。如果这个小时内它没有污染它所在的城市,那麽它随机地选择一条道路,在这个小时内沿着这条道路走到一个新的城市。可以离开这个城市的所有道路被选择的概率均等。因为这个臭气弹的随机的性质,奶牛们很困惑哪个城市最有可能被污染。给定一个猪猡文明的地图和臭气弹在每个小时内爆炸的概率。计算每个城市最终被污染的概率。如下例,假设这个猪猡文明有两个连接在一起的城市。臭气炸弹从城市1出发,每到一个城市,它都有 12 1 2 的概率爆炸。

可知下面这些路径是炸弹可能经过的路径(最后一个城市是臭气弹爆炸的城市): 1 1 : 1 2: 1-2 3 3 : 1-2-1 4: 1-2-1-2 5 5 : 1-2-1-2-1 … 要得到炸弹在城市1终止的概率,我们可以把上面的第 1 1 ,第3,第 5 5 ……条路径的概率加起来,(也就是上表奇数编号的路径)。上表中第k条路径的概率正好是(12)k,也就是必须在前 k1 k − 1 个回合离开所在城市(每次的概率为 112=12 1 − 1 2 = 1 2 )并且留在最后一个城市(概率为 12 1 2 )。所以在城市 1 1 结束的概率可以表示为12+(12)3+(12)5+...。当我们无限地计算把这些项一个个加起来,我们最后会恰好得到 23 2 3 ,也就是我们要求的概率,大约是 0.666666667 0.666666667 。这意味着最终停留在城市 2 2 的概率为13,大约为 0.333333333 0.333333333

输入输出格式

输入格式

第一行四个正整数 N,M,P,Q N , M , P , Q
以下 M M 行, 每行两个正整数Ai,Bi, 表示编号为 Ai A i 的城市和 Bi B i 的城市之间有一条边。

输出格式

一共 N N 行, 每行一个保留9位的小数,第 i i 行的数表示炸弹在第i个城市爆炸的概率。

输入输出样例

输入样例#1
2 1 1 2 
1 2 
输出样例#1
0.666666667 
0.333333333 

解题分析

推一波式子: 设 degA d e g A 为点 A A 的度数, 那么大概递推式是这样的:

PA=Edge(A,B)1PQdegBPB

然后发现这样搞会有环, 那么就高斯消元搞搞就好了。 因为一开始在 1 1 号点,所以一号点的值预先设为1

代码如下:

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <cctype>
#include <algorithm>
#include <vector>
#define R register
#define IN inline
#define W while
#define MX 305
#define gc getchar()
#define db double
template <class T>
IN void in(T &x)
{
    x = 0; R char c = gc;
    for (; !isdigit(c); c = gc);
    for (;  isdigit(c); c = gc)
    x = (x << 1) + (x << 3) + c - 48;
}
std::vector <int> to[MX];
int deg[MX];
db mat[MX][MX], pos;
int dot, line, p, q, bd;

void Gauss()
{
    R int i, j, k;
    for (i = 1; i <= dot; ++i)
    {
        for (j = i + 1; j <= dot; ++j)
        if(fabs(mat[j][i]) > fabs(mat[i][i])) std::swap(mat[j], mat[i]);
        for (j = i + 1; j <= bd; ++j) mat[i][j] /= mat[i][i];
        for (j = 1; j <= dot; ++j)
        {
            if(j ^ i)
            {
                for (k = i + 1; k <= bd; ++k)
                mat[j][k] -= mat[j][i] * mat[i][k];
            }
        }
    }
}
int main(void)
{
    int a, b;
    in(dot), in(line), in(p), in(q); bd = dot + 1;
    pos = 1.0 * p / (1.0 * q);
    for (R int i = 1; i <= line; ++i)
    in(a), in(b), to[a].push_back(b), to[b].push_back(a), ++deg[a], ++deg[b];
    mat[1][bd] = 1;
    for (R int i = 1; i <= dot; ++i)
    {
        mat[i][i] = 1;
        for (R int j = 0; j < deg[i]; ++j)
        mat[i][to[i][j]] = (pos - 1.0) / deg[to[i][j]];
    }
    Gauss();
    for (R int i = 1; i <= dot; ++i) printf("%.9lf\n", mat[i][bd] * pos);

}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值