【jzoj2758】【SDOI2012】【走迷宫】【期望】【高斯消元】

题目大意

Morenan 被困在了一个迷宫里。迷宫可以视为 N 个点 M 条边的有向图,其中 Morena n处于起点 S , 迷宫的终点设为 T 。 可惜的是 , Morenan 非常的脑小 , 他只会从一个点出发随机沿着一条从该点出发的有向边 , 到达另一个点 。 这样 , Morenan 走的步数可能很长 , 也可能是无限,更可能到不了终点。 若到不了终点,则步数视为无穷大。 但你必须想方设法求出 Morenan 所走步数的期望值。

解题思路

倒着求到终点的期望,按拓扑序求,将所有强连通分量求出来缩点,分量内使用高斯消元求解即可。如果有一条路径不能到达终点即输出inf。

code

#include
#include
#include
#include
#define LF double
#define LL long long
#define ULL unsigned long long
#define fo(i,j,k) for(int i=j;i<=k;i++)
#define fd(i,j,k) for(int i=j;i>=k;i--)
#define fr(i,j) for(int i=begin[j];i;i=next[i])
#define f2(i,j) for(int i=begi2[j];i;i=nex2[i])
using namespace std;
int const mn=1e4+9,mm=1e6+9,mp=100+9,inf=1e9+7;
int n,m,s,t,gra,time,begin[mn],to[mm],next[mm],gr2,begi2[mn],t2[mm],nex2[mm],
    dfn[mn],low[mn],inst[mn],st[mn],bel[mn],b[mn],d2[mn],u[mm],v[mm],beg[mn],
    end[mn],du[mn],q[mn],tag[mn],re[mn];
LF f[mn],a[mp][mp],a2[mp];
void insert(int u,int v){
    to[++gra]=v;
    next[gra]=begin[u];
    begin[u]=gra;
}
void inser2(int u,int v){
    t2[++gr2]=v;
    nex2[gr2]=begi2[u];
    begi2[u]=gr2;
}
void tarjan(int now){
    dfn[now]=low[now]=++time;
    inst[st[++st[0]]=now]=1;
    fr(i,now)if(!dfn[to[i]]){
        tarjan(to[i]);
        low[now]=min(low[now],low[to[i]]);
    }else if(inst[to[i]])low[now]=min(low[now],dfn[to[i]]);
    if(dfn[now]==low[now]){
        while(st[st[0]]!=now)bel[st[st[0]]]=now,inst[st[st[0]--]]=0;
        bel[st[st[0]]]=now,inst[st[st[0]--]]=0;
    }
}
void dfs(int now){
    tag[now]=1;
    fr(i,now)if(!tag[to[i]])dfs(to[i]);
}
bool cmp(int x,int y){
    return bel[x]y];
}
void solve(int l,int r){
    int n=r-l+1;
    fo(i,1,n)fo(j,1,n+1)a[i][j]=0;
    fo(i,l,r){
        int now=b[i];
        a[i-l+1][i-l+1]=1;a[i-l+1][n+1]=1;
        fr(j,now)if(bel[now]==bel[to[j]])
            a[i-l+1][re[to[j]]-l+1]+=-1.0/d2[now];
        else 
            a[i-l+1][n+1]+=f[to[j]]/d2[now];
    }
    fo(i,1,n){
        if(!a[i][i]){
            fo(j,i+1,n)if(a[j][i])
                fo(k,i,n+1)swap(a[i][k],a[j][k]);
        }
        fo(j,i+1,n){
            LF tmp=a[j][i]/a[i][i];
            fo(k,i,n+1)a[j][k]-=a[i][k]*tmp;
        }
    }
    fd(i,n,1){
        a2[i]=a[i][n+1];
        fo(j,i+1,n)a2[i]-=a[i][j]*a2[j];
        a2[i]/=a[i][i];
    }
    fo(i,l,r)f[b[i]]=a2[i-l+1];
}
int main(){
    freopen("d.in","r",stdin);
    freopen("d.out","w",stdout);
    scanf("%d%d%d%d",&n,&m,&s,&t);
    int tmp=0;
    fo(i,1,m){
        tmp++;
        scanf("%d%d",&u[tmp],&v[tmp]);
        if(u[tmp]==t){tmp--;continue;}
        insert(u[tmp],v[tmp]);d2[u[tmp]]++;
    }
    m=tmp;
    fo(i,1,n)if(!dfn[i])tarjan(i);
    fo(i,1,n)b[i]=i;
    sort(b+1,b+n+1,cmp);
    fo(i,1,n)re[b[i]]=i;
    int i=1;
    while(i<=n){
        int j=i;
        while(bel[b[i]]==bel[b[j]])j++;
        beg[bel[b[i]]]=i;
        end[bel[b[i]]]=j-1;
        i=j;
    }
    fo(i,1,m)if(bel[u[i]]!=bel[v[i]])inser2(bel[u[i]],bel[v[i]]),du[bel[v[i]]]++;
    int he=0,ti=0,ok=1;
    fo(i,1,n)if((bel[i]==i)&&(!du[i]))q[++ti]=i;
    dfs(s);
    while(he!=ti){
        int now=q[++he],tmp=ti;
        f2(i,now)if((--du[t2[i]])==0)q[++ti]=t2[i];
        if((!begi2[now])&&(now!=bel[t]))ok=0;
    }
    fd(i,ti-1,1)
        solve(beg[q[i]],end[q[i]]);
    if(tag[t]&&ok)printf("%.3lf\n",f[s]-f[t]);
    else printf("INF");
    //fo(i,1,n)printf("%.10lf\n",f[i]);
    return 0;
}

你可能感兴趣的:(期望,jzoj,数论)