[SDOI2012]走迷宫

题意

给你一个有向图 , , 问从 S S 走到 T T 的期望步数

如果存在一条路径从 S S 出发走不到 T, T , 那么期望就是 inf inf

保证每个强连通分量大小 100 ≤ 100


题解

先考虑怎么判无解

如果一个点满足 S S 能到 , , 且他没有出度 , , 那么就一定无解

如果 S S 不能到 T T 一样无解

无向图很好做直接列 n n 个方程然后高消即可

考虑有向图怎么做 , , 首先先 tarjan t a r j a n 缩环

还是考虑经典的 dp,f[u] d p , f [ u ] 表示 u u 走到 T T 的期望步数 ,dg[u] , d g [ u ] 表示 u u 的出度

f[u]=1dg[u]uvEf[v]+1 ⇒ f [ u ] = 1 d g [ u ] ∑ u → v ∈ E f [ v ] + 1

1. 1. 如果 u,v u , v 不属于同一个强连通分量那么 dp d p 是可以直接转移的

2. 2. 如果 u,v u , v 在一个强连通分量那么转移可能成环

所以考虑对于同一个强联通分量里的点高斯消元

考虑到 f[u] f [ u ] 可能已经有值 , , 并且不是所有的 uvE u → v ∈ E 都满足 belongu=belongv b e l o n g u = b e l o n g v

所以每个方程的第 n+1 n + 1 项应该是 f[u]+1dg[u]uvE[belongu=belongv] f [ u ] + 1 d g [ u ] ∑ u → v ∈ E [ b e l o n g u = b e l o n g v ]

#include
#define fp(i,a,b) for(register int i=a,I=b+1;i
#define fd(i,a,b) for(register int i=a,I=b-1;i>I;--i)
#define go(u) for(register int i=fi[u],v=e[i].to;i;v=e[i=e[i].nx].to)
#define Go(u) for(register int i=Fi[u],v=E[i].to;i;v=E[i=E[i].nx].to)
#define file(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout)
template<class T>inline bool cmax(T&a,const T&b){return a1:0;}
template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
using namespace std;
char ss[1<<17],*A=ss,*B=ss;
inline char gc(){return A==B&&(B=(A=ss)+fread(ss,1,1<<17,stdin),A==B)?-1:*A++;}
template<class T>inline void sd(T&x){
    char c;T y=1;while(c=gc(),(c<48||571)if(c==45)y=-1;x=c-48;
    while(c=gc(),4758)x=x*10+c-48;x*=y;
}
const int N=1e4+5,Eg=1e6+5,M=105;
const double eps=1e-13;
typedef int arr[N];
typedef double db;
struct eg{int nx,to;}e[Eg],E[Eg];
int n,m,dft,Cnt,Top,p[N][M];
arr fi,Fi,bl,in,id,dfn,low,vis,S,Sum;db f[N],dg[N],ans[M],G[M][M];
void tarjan(int u){
    dfn[u]=low[u]=++dft;S[++Top]=u;vis[u]=1;
    go(u)
        if(!dfn[v])tarjan(v),cmin(low[u],low[v]);
        else if(vis[v])cmin(low[u],dfn[v]);
    if(dfn[u]==low[u]){
        int v;++Cnt;
        do{
            bl[v=S[Top--]]=Cnt;vis[v]=0;
            p[Cnt][id[v]=++Sum[Cnt]]=v;
        }while(v^u);
    }
}
inline void add(int u,int v){static int ce=0;e[++ce]={fi[u],v},fi[u]=ce;}
inline void ADD(int u,int v){static int ce=0;E[++ce]={Fi[u],v},Fi[u]=ce;}
inline int cmp(db x){return fabs(x)0:x<0?-1:1;}
inline void Gauss(int n){
    db t;int mx;
    fp(i,1,n){mx=i;
        fp(j,i,n)if(cmp(G[mx][i]-G[j][i])<0)mx=j;
        if(mx^i)fp(j,i,n+1)swap(G[mx][j],G[i][j]);
        fp(j,i+1,n){
            t=G[j][i]/G[i][i];
            fp(k,i,n+1)G[j][k]-=G[i][k]*t;
        }
    }
    fd(i,n,1){
        fp(j,i+1,n)G[i][n+1]-=G[i][j]*ans[j];
        ans[i]=G[i][n+1]/G[i][i];
    }
    fp(i,1,n)fp(j,1,n+1)G[i][j]=0;
}
inline void Topsort(int Pos){
    register int h=1,t=1,nw,u,s;
    static int q[N];q[1]=bl[Pos];
    fp(u,1,n)Go(u)if(bl[u]^bl[v])++in[bl[v]];
    while(h<=t){
        nw=q[h++];s=Sum[nw];
        fp(x,1,s){
            u=p[nw][x];G[x][x]=1,G[x][s+1]=f[u];
            if(u==Pos)continue;
            go(u)if(bl[u]==bl[v])
                G[x][s+1]+=dg[u],
                G[x][id[v]]-=dg[u];
        }
        Gauss(s);
        fp(x,1,s){
            u=p[nw][x],f[u]=ans[x];
            Go(u)if(bl[u]^bl[v]){
                if(!(--in[bl[v]]))q[++t]=bl[v];
                f[v]+=(f[u]+1)*dg[v];
            }
        }
    }
}
int main(){
    #ifndef ONLINE_JUDGE
        file("s");
    #endif
    int u,v,S,T;
    sd(n),sd(m),sd(S),sd(T);
    while(m--)sd(u),sd(v),add(u,v),ADD(v,u),++dg[u];
    tarjan(S);
    fp(i,1,n)
        if(i^T){if(dfn[i]&&!dg[i])return puts("INF"),0;if(dg[i])dg[i]=1/dg[i];}
        else if(!dfn[i])return puts("INF"),0;
    Topsort(T);
    printf("%.3lf\n",f[S]);
return 0;
}

你可能感兴趣的:(DP,概率期望,高斯消元,Tarjan,拓扑排序)