BZOJ2707: [SDOI2012]走迷宫

题目:http://www.lydsy.com/JudgeOnline/problem.php?id=2707

首先找出所有强联通分量然后缩点,然后从开始点的那个联通块开始dfs,如果发现它走到了死路,那么期望步数就是无限大。

然后判断掉无限大的情况后我们对每个scc求期望,对于scc中的每个点,由E[u]=ΣE[v]*1/d[u]+1,联立方程高斯消元。(如果连边连到scc外,就让常数项加上E[x]/d[u])

注意E[t]=0,然后要把t的所有出边全部砍掉。但是建方程的时候其系数还要是1。。。

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cmath>
#include<stack>
#include<cstring>
#define rep(i,l,r) for (int i=l;i<=r;i++)
#define down(i,l,r) for (int i=l;i>=r;i--)
#define clr(x,y) memset(x,y,sizeof(x))
#define maxm 1005000
#define maxn 105000
#define eps 1e-6
using namespace std;
stack<int >q;
struct data{int obj,pre;
}e[maxm];
int head[maxn],low[maxn],dfn[maxn],vis[maxn],num[maxn],d[maxn],size[maxn],rank[maxn],cal[maxn];
int b[maxn][205];
double a[205][205],ex[maxn];
int idx,tot,scc,n,m,s,t;
int read(){
    int x=0,f=1; char ch=getchar();
    while (!isdigit(ch)) {if (ch=='-') f=-1; ch=getchar();}
    while (isdigit(ch)) {x=x*10+ch-'0'; ch=getchar();}
    return x*f;
}
void insert(int x,int y){
    e[++tot].obj=y; e[tot].pre=head[x]; head[x]=tot;
}
void tarjan(int u){
    dfn[u]=low[u]=++idx; q.push(u);
    for (int j=head[u];j;j=e[j].pre){
        int v=e[j].obj;
        if (!dfn[v]){
            tarjan(v); 
            low[u]=min(low[u],low[v]);
        }
        else if (!num[v]) low[u]=min(low[u],dfn[v]);
    }
    if (low[u]==dfn[u]){
        int x=-1,cnt=0; ++scc;
        while (x!=u){
            x=q.top(); q.pop();
            num[x]=scc;
            rank[x]=++cnt;
            size[scc]++;
            b[scc][size[scc]]=x;
        }
    }
}
void dfs(int x){
    vis[x]=0;
    if (x==num[t]){
        vis[x]=1; return ;
    }
    rep(i,1,size[x]){
        int u=b[x][i];
        for (int j=head[u];j;j=e[j].pre){
            int v=e[j].obj;
            if (num[v]==x) continue;
            if (vis[num[v]]==-1) dfs(num[v]);
            if (vis[num[v]]==1) vis[x]=1; 
        }
    }    
}
void gs(int n){
    int x;
    rep(i,1,n){
        x=i; while (fabs(a[x][i])<eps&&x<=n) x++;
         if (x>n) continue;
         rep(j,1,n+1) swap(a[i][j],a[x][j]);
         double t=a[i][i];
         rep(j,1,n+1) a[i][j]/=t;
         rep(j,1,n) if (j!=i){
             double t=a[j][i];
             rep(k,1,n+1) a[j][k]-=t*a[i][k];
         }
    }
}
void solve(int x){
    rep(i,1,size[x]){
        int u=b[x][i];
        for (int j=head[u];j;j=e[j].pre){
            int v=e[j].obj;
            if (num[v]!=x&&!cal[num[v]]) solve(num[v]); 
        }
    }
    clr(a,0);
    rep(i,1,size[x]){
        a[i][i]=1.0;
        int u=b[x][i]; 
        if (u==t) continue;
        a[i][size[x]+1]=1.0;
        for (int j=head[u];j;j=e[j].pre){
            int v=e[j].obj;
            if (num[v]==x) a[i][rank[v]]-=1.0/d[u];
            else a[i][size[x]+1]+=1.0/d[u]*ex[v]; 
        }
    }
    gs(size[x]);
    rep(i,1,size[x]) ex[b[x][i]]=a[rank[b[x][i]]][size[x]+1];
    cal[x]=1; 
}
int main(){
    n=read(); m=read(); s=read(); t=read();
    if (s==t){
        puts("0.000"); return 0;
    }
    rep(i,1,m){
        int x,y;
        x=read(); y=read(); insert(x,y); d[x]++;
    }
    rep(i,1,n){
        if (!dfn[i]) tarjan(i);
    }
    
    clr(vis,-1);
    dfs(num[s]);
    rep(i,1,scc) if (vis[i]==0) {
        puts("INF"); return 0;
    }
    
    solve(num[s]);
    printf("%.3lf\n",ex[s]);
    return 0;
}

 

posted on 2015-11-30 21:21  ctlchild  阅读(331)  评论(0编辑  收藏  举报

导航