其他分享
首页 > 其他分享> > [SDOI2012]走迷宫 强连通+概率DP+高斯消元

[SDOI2012]走迷宫 强连通+概率DP+高斯消元

作者:互联网

题目

([SDOI2012]走迷宫) https://ac.nowcoder.com/acm/problem/20575

题目描述

Morenan被困在了一个迷宫里。迷宫可以视为N个点M条边的有向图,其中Morenan处于起点S,迷宫的终点设为T。可惜的是,Morenan非常的脑小,他只会从一个点出发随机沿着一条从该点出发的有向边,到达另一个点。这样,Morenan走的步数可能很长,也可能是无限,更可能到不了终点。若到不了终点,则步数视为无穷大。但你必须想方设法求出Morenan所走步数的期望值。
n<10000,m<1000000,每个SCC大小不超过100
输入描述:
第1行4个整数,N,M,S,T第[2, M+1]行每行两个整数o1, o2,表示有一条从o1到o2的边。
输出描述:
一个浮点数,保留小数点3位,为步数的期望值。若期望值为无穷大,则输出"INF"。

样例一

输入
6 6 1 6
1 2
1 3
2 4
3 5
4 6
5 6
输出
3.000

样例二

输入
9 12 1 9
1 2
2 3
3 1
3 4
3 7
4 5
5 6
6 4
6 7
7 8
8 9
9 7
输出
9.500

样例三

输入
2 0 1 2
输出
INF

思路

我们先考虑INF的情况。如果存在一个点X不能到达T,但是S能够到达X。那么就是是INF。我们把边取反。那么T能够
到达的点就是能够到达T的点,再DFS(S)一次。就可以判断INF。

然后。我们知道期望反推

但是存在一个问题,就是n太大,直接高斯消元会T。但是每个scc<=100,我们把图缩点。从T的scc开始。按拓扑反序进行一个一个scc的高斯消元。
对于同一个scc的边,按上面的公式就可以了,如果\(u->to。scc[u]!=scc[to]\),那么f[to]一定知道了。就是常数项\(+(f[to]+1)/G[u].sie()\)

F[T]=0, 那么F[s]就是答案。复杂度O(n^4)

坑点:重边,有自环

#include<bits/stdc++.h>
#define LL long long
using namespace std;

struct Edge {
    int from, to, nxt;
} e[2000005];
int head[100005], cut=0, N=0;
int scc[100005];
vector<int> g[100005];
struct Scc {
    int dfn[100005], low[100005], vis[100005], T=0;
    stack<int> sk;
    void Addedge(int x, int y) {
        e[++cut]= {x, y, head[x]};
        head[x]=cut;
    }
    void Ta(int u) {
        dfn[u]=low[u]=++T;
        sk.push(u);
        vis[u]=1;
        for(int i=head[u]; i; i=e[i].nxt) {
            int to=e[i].to;
            if(!dfn[to]) {
                Ta(to);
                low[u]=min(low[u], low[to]);
            } else if(vis[to]) {
                low[u]=min(low[u], dfn[to]);
            }
        }
        if(low[u]==dfn[u]) {
            N++;
            while(1) {
                int now=sk.top();
                sk.pop();
                vis[now]=0;
                scc[now]=N;
                g[N].push_back(now);
                if(now==u) {
                    break;
                }
            }
        }
    }
} sc;

int d[200005];
vector<int> G[200005], Gf[200005];

const double eps=1e-5;
double a[205][205];
double ans[205];
map<int, int> mp;
void Gauss(int n) {
    for(int i=1; i<=n; ++i) {
        int r=i;
        for(int j=i+1; j<=n; ++j) //找主元系数的绝对值最大的一行
            if(fabs(a[j][i])>fabs(a[r][i]))
                r=j;
        if(fabs(a[r][i])<eps) {  //最大为0,无解
            printf("No Solution\n");
            return;
        }
        if(r!=i)
            for(int j=1; j<=n+1; ++j)
                swap(a[i][j],a[r][j]);
        for(int j=1; j<=n; ++j)  //消元
            if(j!=i) {
                double tmp=a[j][i]/a[i][i];
                for(int k=i; k<=n+1; ++k)
                    a[j][k]-=a[i][k]*tmp;
            }
    }
    for(int i=1; i<=n; ++i)
        ans[i]=a[i][n+1]/a[i][i];
}

queue<int> q;
int vis[200005];
vector<int> c;
void dfs(int u){
    vis[u]=1;
    for(auto x: Gf[u]){
        if(!vis[x]){
            dfs(x);
        }
    }
}

int dfsinf(int u, int t){
    if(u==t) return 1;
    if(vis[u]==0) return 0;
    vis[u]++;
    for(int i=head[u]; i; i=e[i].nxt){
        int x=e[i].to;
        if(vis[x]<2){
            if(!dfsinf(x, t)) return 0;
        }
    }
    return 1;
}

double f[100005];
void dfs(int u, int N) {//每个scc构建高斯消元
    int sz=0;
    vis[u]=1;
    c.push_back(u);
    for(int i=head[u]; i; i=e[i].nxt) {
        sz++;
    }
    for(int i=head[u]; i; i=e[i].nxt) {
        int to=e[i].to;
        if(scc[to]==scc[u]) {
            a[mp[u]][mp[to]]+=-1.0/sz;
            if(!vis[to]) {
                dfs(to, N);
            }
        }
    }
    a[mp[u]][mp[u]]+=1;
    a[mp[u]][N+1]=1;
}

int main() {
    int n, m, s, t;
    scanf("%d%d%d%d", &n, &m, &s, &t);
    for(int i=1; i <=m; i++) {
        int x, y;
        scanf("%d%d", &x, &y);
        sc.Addedge(x, y);
        Gf[y].push_back(x);
    }
    for(int i=1; i<=n; i++) {
        if(!sc.dfn[i]) {
            sc.Ta(i);
        }
    }
    for(int i=1; i<=m; i++) {
        int x=scc[e[i].from], y=scc[e[i].to];
        if(x!=y) {
            d[x]++;
            G[y].push_back(x);
        }
    }

    dfs(t);
    if(!dfsinf(s, t)) {
        printf("INF\n");
        return 0;
    }
    memset(vis, 0, sizeof(vis));

    q.push(scc[t]);
    while(!q.empty()) {//反拓扑序dp
        int now=q.front();
        q.pop();
        mp.clear();
        int N=0;
        for(auto x: g[now]) {
            mp[x]=++N;
        }
        for(auto x: c){
            vis[x]=0;
        }
        c.clear();
        memset(a, 0, sizeof(a));
        dfs(g[now][0], N);

        for(auto x: g[now]) {
            int sz=0;
            for(int i=head[x]; i; i=e[i].nxt) {
                sz++;
            }
            for(int i=head[x]; i; i=e[i].nxt) {
                int y=e[i].to;
                if(scc[x]!=scc[y]) {
                    a[mp[x]][N+1]+=(f[y])/sz;
                }
            }
        }
        if(now==scc[t]) {//f[t]=0
            for(int i=1; i<=N+1; i++) {
                a[mp[t]][i]=0;
            }
            a[mp[t]][mp[t]]=1;
        }

        Gauss(N);
        for(auto x: g[now]) {
            f[x]=ans[mp[x]];
        }

        for(auto x: G[now]) {
            d[x]--;
            if(d[x]==0) {
                q.push(x);
            }
        }
    }
    printf("%.3f\n", fabs(f[s]));

    return 0;
}
/*
8 12 2 4
3 6
4 1
6 8
3 1
2 6
5 4
5 2
8 1
7 3
1 4
2 6
8 7
*/

标签:int,scc,100005,vis,SDOI2012,高斯消,now,DP,low
来源: https://www.cnblogs.com/liweihang/p/13644282.html