【BZOJ】3640: JC的小苹果

题解

我们考虑列出期望方程组,\(dp[i][j]\)表示在第\(i\)个点血量为\(j\)的时候到达\(N\)点的概率,所有的\(dp[N][j]\)都是1,所有\(j < 0\)都是0
答案是\(dp[1][hp]\)

\(dp[u][j] = \sum_{v} \frac{1}{deg[u]}dp[v][j - a[v]]\)
我们发现这个方程在j不同的时候,只有常数项发生改变,剩下的系数不变

于是我们把常数项变成一个列向量,把需要消元的系数矩阵求一个逆,每次计算常数项,用常数项乘逆矩阵得到每一个血量的\(dp\)值即可

注意判自环= =

代码

#include <bits/stdc++.h>
#define fi first
#define se second
#define pii pair<int,int>
#define pdi pair<db,int>
#define mp make_pair
#define pb push_back
#define enter putchar('\n')
#define space putchar(' ')
#define eps 1e-8
#define mo 974711
#define MAXN 100005
//#define ivorysi
using namespace std;
typedef long long int64;
typedef double db;
template<class T>
void read(T &res) {
    res = 0;char c = getchar();T f = 1;
    while(c < '0' || c > '9') {
    if(c == '-') f = -1;
    c = getchar();
    }
    while(c >= '0' && c <= '9') {
    res = res * 10 + c - '0';
    c = getchar();
    }
    res *= f;
}
template<class T>
void out(T x) {
    if(x < 0) {x = -x;putchar('-');}
    if(x >= 10) {
    out(x / 10);
    }
    putchar('0' + x % 10);
}
int N,M,hp,deg[155],blood[155];
struct node {
    int to,next;
}E[100005];
int sumE,head[155];
db b[155],dp[155][10005],c[155];
void add(int u,int v) {
    E[++sumE].next = head[u];
    E[sumE].to = v;
    head[u] = sumE;
}
struct Matrix {
    db f[155][155];
    Matrix() {memset(f,0,sizeof(f));}
    void unit() {
    for(int i = 1 ; i <= N ; ++i) {
        f[i][i] = 1.0;
    }
    }
    friend Matrix operator * (const Matrix &a,const Matrix &b) {
    Matrix c;
    for(int i = 1 ; i <= N ; ++i) {
        for(int j = 1 ; j <= N ; ++j) {
        for(int k = 1 ; k <= N ; ++k) {
            c.f[i][j] += a.f[i][k] * b.f[k][j];
        }
        }
    }
    return c;
    }
    friend Matrix operator ~(Matrix a) {
    Matrix b;
    b.unit();
    for(int i = 1 ; i <= N ; ++i) {
        int l = i;
        for(int j = i + 1; j <= N ; ++j) {
        if(fabs(a.f[j][i]) > fabs(a.f[l][i])) l = j;
        }
        if(i != l) {
        for(int j = 1 ; j <= N ; ++j) {
            swap(a.f[i][j],a.f[l][j]);
            swap(b.f[i][j],b.f[l][j]);
        }
        }
        db t = 1.0 / a.f[i][i];
        for(int j = 1 ; j <= N ; ++j) {
        a.f[i][j] *= t;
        b.f[i][j] *= t;
        }
        
        for(int j = 1 ; j <= N ; ++j) {
        if(i == j) continue;
        db t = a.f[j][i];
        for(int k = 1 ; k <= N ; ++k) {
            a.f[j][k] -= t * a.f[i][k];
            b.f[j][k] -= t * b.f[i][k];
        }
        }
    }
    return b;
    }
}A;
void Solve() {
    read(N);read(M);read(hp);
    int u,v;
    for(int i = 1 ; i <= N ; ++i) read(blood[i]);
    for(int i = 1 ; i <= M ; ++i) {
    read(u);read(v);
    if(u == v) {add(u,v);++deg[u];}
    else {add(u,v);add(v,u);++deg[u];++deg[v];}
    }
    for(int u = 1 ; u <= N ; ++u) {
    if(u == N) {
        b[u] = 1;A.f[u][u] = 1;continue;
    }
    A.f[u][u] = 1;
    for(int i = head[u] ; i ; i = E[i].next) {
        int v = E[i].to;
        if(v == N) {b[u] += 1.0 / deg[u];}
        else if(blood[v] == 0){
        A.f[v][u] -= 1.0 / deg[u];
        }
    }
    }
    A = ~A;
    for(int i = 1 ; i <= hp ; ++i) {
    for(int u = 1 ; u <= N ; ++u) {
        c[u] = b[u];
        if(u == N) continue;
        for(int h = head[u] ; h ; h = E[h].next) {
        int v = E[h].to;
        if(blood[v] && i > blood[v]) {
            c[u] += (1.0 / deg[u]) * dp[v][i - blood[v]];
        }
        }
    }
    for(int u = 1 ; u <= N ; ++u) {
        for(int k = 1 ; k <= N ; ++k) {
        dp[u][i] += c[k] * A.f[k][u];
        }
    }
    }
    printf("%.8lf\n",dp[1][hp]);
}
int main() {
#ifdef ivorysi
    freopen("f1.in","r",stdin);
#endif
    Solve();
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/ivorysi/p/10090841.html
jc