[SDOI2012]走迷宫

题意

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

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

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


题解

先考虑怎么判无解

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

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

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

考虑有向图怎么做 , , 首先先tarjan缩环

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

f[u]=1dg[u]uvEf[v]+1

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

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

考虑到 f[u] f [ u ] 可能已经有值 , , 并且不是所有的uvE都满足 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<bits/stdc++.h>
#define fp(i,a,b) for(register int i=a,I=b+1;i<I;++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 a<b?a=b,1: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||57<c)&&c!=-1)if(c==45)y=-1;x=c-48;
    while(c=gc(),47<c&&c<58)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)<eps?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;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值