2707: [SDOI2012]走迷宫 tarjan+高斯消元解期望方程组

SDOI2012啊,不错的一道题。
点数很多,我们不能直接高斯消元,而题目中提示了每个强连通分量的点数 <=100 <script type="math/tex" id="MathJax-Element-23"><=100</script>,我们就可以先tarjan缩一下环。
先说一下无解的情况,如果有一个强连通分量到不了 T ,就可以在这个强连通分量内无限的走,答案为INF,dfs一下就可以解决。
考虑有解的情况,这就成了DAG上的期望DP问题,我们可以用 fi 表示从 i 走到T的期望步数,初始 fT=0 ,那么 fi 可以在 dfs 的过程中由点 i 的后继转移而来,但是每个强连通分量内的点我们要用高斯消元来求,由于不超过100所以高斯消元完全可以解决。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#define N 10005
#define M 1000005
using namespace std;
vector<int> V[N];
double f[N],a[105][105];
int n,m,S,T,tot,top,cnt,scc;
int dfn[N],low[N],belong[N],du[N],rank[N],vis[N],stack[N],head[N];
int list[M],next[M];
bool inset[N];
inline int read()
{
    int a=0,f=1; char c=getchar();
    while (c<'0'||c>'9') {if (c=='-') f=-1; c=getchar();}
    while (c>='0'&&c<='9') {a=a*10+c-'0'; c=getchar();}
    return a*f;
}
inline void insert(int x,int y)
{
    next[++cnt]=head[x];
    head[x]=cnt;
    list[cnt]=y;
    du[x]++;
}
void tarjan(int x)
{
    dfn[x]=low[x]=++tot;
    stack[++top]=x;
    inset[x]=true;
    for (int i=head[x];i;i=next[i])
        if (!dfn[list[i]])
        {
            tarjan(list[i]);
            low[x]=min(low[x],low[list[i]]);
        }
        else if (inset[list[i]]) low[x]=min(low[x],dfn[list[i]]);
    if (dfn[x]==low[x])
    {
        int i=-1;
        scc++;
        while (i!=x)
        {
            i=stack[top--];
            belong[i]=scc;
            rank[i]=V[scc].size();
            V[scc].push_back(i);
            inset[i]=false;
        }
    }
}
void dfs(int x)
{
    vis[x]=2;
    if (x==belong[T]) 
    {
        vis[x]=1;
        return;
    }
    for (int i=0;i<V[x].size();i++)
        for (int j=head[V[x][i]];j;j=next[j])
            if (belong[list[j]]!=x)
            {
                if (!vis[belong[list[j]]]) 
                    dfs(belong[list[j]]);
                if (vis[belong[list[j]]]==1) 
                    vis[x]=1;
            }
}
void get_ans(int x)
{
    int n=V[x].size();
    for (int i=0;i<n;i++)
        for (int j=head[V[x][i]];j;j=next[j])
            if (belong[list[j]]!=x&&!vis[belong[list[j]]]) 
                get_ans(belong[list[j]]);
    memset(a,0,sizeof(a));
    for (int i=0;i<n;i++)
    {
        a[i][i]=1;
        if (V[x][i]==T) continue;
        a[i][V[x].size()]=1;
        for (int j=head[V[x][i]];j;j=next[j])
            if (belong[list[j]]==x) 
                a[i][rank[list[j]]]-=1.0/du[V[x][i]];
            else a[i][V[x].size()]+=1.0/du[V[x][i]]*f[list[j]];
    }
    for (int i=0;i<n;i++)
    {
        double t=a[i][i];
        for (int j=0;j<=n;j++) 
            a[i][j]/=t;
        for (int j=0;j<n;j++)
            if (j!=i) 
            {
                double t=a[j][i];
                for (int k=0;k<=n;k++) 
                    a[j][k]-=t*a[i][k];
            }
    }
    for (int i=0;i<n;i++)
        f[V[x][i]]=a[i][n];
    vis[x]=1;
}
int main()
{
    n=read(); m=read(); S=read(); T=read();
    for (int i=1;i<=m;i++)
    {
        int u=read(),v=read();
        insert(u,v); 
    }
    for (int i=1;i<=n;i++)
        if (!dfn[i]) tarjan(i);
    memset(vis,0,sizeof(vis));
    dfs(belong[S]);
    for (int i=1;i<=scc;i++)
        if (vis[i]==2) 
        {
            puts("INF");
            return 0;
        }
    memset(vis,0,sizeof(vis));
    get_ans(belong[S]);
    printf("%.3lf\n",f[S]);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值