【bzoj2707】走迷宫

Portal --> bzoj2707

Solution

  首先题目有一个十分明显的暗示。。强联通分量。。那肯定就是要tarjan一波咯

  先看看什么情况下会\(INF\),其实就是题目里面讲的两种:
  一种是根本走不到出口,一种是存在一个点,起点能够走到这个点但是这个点走不到出口

  具体判断方式的话分别从起点和出口跑两遍dfs就好了

​  

  对于不是\(INF\)的情况,考虑用dp来算到达每个点的期望步数

  用\(f[i]\)表示\(i\)走到终点的期望步数,\(du[i]\)表示\(i\)点的出度,对于不为出口的点,可以得到式子
\[ f[i]=1+\sum f[u]*\frac{1}{du[i]} \]
  其中\(u\)满足原图中存在一条\((i,u)\)的边

  对于出口\(T\)则有\(f[T]=0\)

  然后每个点的式子是一样的,我们把\(f[i]\)看成未知数,就可以考虑用高斯消元解方程组来解决问题

  然而很尴尬的事情是\(n<=10000\),直接消元愉快爆炸qwq

  

  注意到题目那个给的很奇怪的条件:强联通分量的大小\(<=100\),而对于\(100\)的规模是可以高斯消元的

  所以我们可以将图中的强联通分量分别缩点,然后对每个强联通分量消元

  那对于一个强联通分量里面直接消消不出来的未知数怎么办呢?我们考虑按照一个特定的顺序来处理每个强联通分量,也就是按照缩点后的拓扑序逆序来消,这样可以保证到消到一个强联通分量的时候,里面涉及到的连出去的点的\(f\)值已经被处理出来了,方程数量和包含的未确定的\(f\)值的数量相同,这样在消元的时候就不会出现问题了

​  

  代码大概长这样(小trick在消元的时候两边乘上出度把分母搞掉会比较舒服一点)

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<cmath>
#define db double
using namespace std;
const int MAXN=10010;
struct xxx{
    int y,nxt;
}a[1000010*2];
int h[MAXN],viscnt[MAXN],vis[MAXN],dfn[MAXN],low[MAXN];
int id[MAXN],belong[MAXN];
int st[MAXN],inst[MAXN],ind[MAXN],outd[MAXN],nind[MAXN],noutd[MAXN];
vector<int>dian[MAXN],na[MAXN],ina[MAXN];
db A[110][110],f[MAXN];
int n,m,tot,T,num,dfn_t,top,S,visT;
void add(int x,int y);
void dfs(int x);
void tarjan(int x);
void prework();
bool check();
void checkdfs1(int x);
void checkdfs2(int x);
void solve();
void solvedfs(int x);
void gauss(int b);
void fill(int b);

int main(){
#ifndef ONLINE_JUDGE
    freopen("a.in","r",stdin);
#endif
    int x,y;
    scanf("%d%d%d%d",&n,&m,&S,&T);
    memset(h,-1,sizeof(h));
    tot=0;
    for (int i=1;i<=m;++i){
        scanf("%d%d",&x,&y);
        if (x==T) continue;
        ++ind[y]; ++outd[x];
        add(x,y);
    }
    prework();
    solve();
}

void prework(){
    num=0;
    for (int i=1;i<=n;++i)
        if (dfn[i]==0) tarjan(i);
    int u;
    for (int i=1;i<=n;++i){
        for (int j=h[i];j!=-1;j=a[j].nxt){
            u=a[j].y;
            if (belong[i]!=belong[u]){
                na[belong[i]].push_back(belong[u]);
                ina[belong[u]].push_back(belong[i]);
            }
        }
    }
}

void add(int x,int y){
    a[++tot].y=y; a[tot].nxt=h[x]; h[x]=tot;
}

void tarjan(int x){
    int u;
    dfn[x]=++dfn_t; low[x]=dfn[x]; st[++top]=x; inst[x]=true;
    for (int i=h[x];i!=-1;i=a[i].nxt){
        u=a[i].y;
        if (dfn[u]==0){
            tarjan(u);
            low[x]=min(low[u],low[x]);
        }
        else if (inst[u])
            low[x]=min(low[x],dfn[u]);
    }
    if (low[x]==dfn[x]){
        ++num;
        int tmpcnt=0;
        do{
            u=st[top--]; inst[u]=false;
            id[u]=++tmpcnt; nind[num]+=ind[u]; noutd[num]+=outd[u];
            belong[u]=num;
            dian[num].push_back(u);
        }while (u!=x);
    }
}

void checkdfs1(int x){
    int u;
    ++viscnt[x]; vis[x]=visT;
    for (int i=0;i<na[x].size();++i)
        if (vis[na[x][i]]!=visT) 
            checkdfs1(na[x][i]);
}

void checkdfs2(int x){
    int u;
    viscnt[x]+=2; vis[x]=visT;
    for (int i=0;i<ina[x].size();++i)
        if (vis[ina[x][i]]!=visT)
            checkdfs2(ina[x][i]);
}

bool check(){
    memset(vis,0,sizeof(vis));
    ++visT; checkdfs1(belong[S]);
    ++visT; checkdfs2(belong[T]);
    for (int i=1;i<=num;++i) if (viscnt[i]==1) return false;
    return true;
}

void solve(){
    if (!check()){printf("INF\n");return;}
    ++visT;
    solvedfs(belong[S]);
    printf("%.3lf\n",f[S]);
}

void solvedfs(int x){
    vis[x]=visT;
    for (int i=0;i<na[x].size();++i)
        if (vis[na[x][i]]!=visT)
            solvedfs(na[x][i]);
    gauss(x);
}

void fill(int b){
    int sz=dian[b].size(),x,u;
    memset(A,0,sizeof(A));
    for (int i=0;i<sz;++i){
        x=dian[b][i];
        A[i][sz]=1*outd[x];//all *outd[x];
        for (int j=h[x];j!=-1;j=a[j].nxt){
            u=a[j].y;
            if (belong[u]==b)
                A[i][id[u]-1]--;
            else
                A[i][sz]+=f[u];
        }
        A[i][i]+=outd[x];
    }
}

void gauss(int b){
    if (b==belong[T]){f[T]=0; return;}
    fill(b);
    int id,n=dian[b].size();
    db tmp;
    for (int i=0;i<n;++i){
        id=i;
        for (int j=i+1;j<n;++j)
            if (fabs(A[j][i])>fabs(A[id][i])) id=j;
        if (id!=i)
            for (int j=0;j<=n;++j) swap(A[id][j],A[i][j]);
        for (int j=i+1;j<n;++j){
            tmp=A[j][i]/A[i][i];
            for (int k=i;k<=n;++k)
                A[j][k]-=tmp*A[i][k];
        }
    }
    for (int i=n-1;i>=0;--i){
        for (int j=n-1;j>i;--j)
            A[i][n]-=A[i][j]*A[j][n];
        A[i][n]/=A[i][i];
    }
    for (int i=0;i<n;++i)
        f[dian[b][i]]=A[i][n];
}

猜你喜欢

转载自www.cnblogs.com/yoyoball/p/9088141.html
今日推荐