X.[SDOI2012]走迷宫
这题本来是一个SCC+高斯消元的模板题来着的……但关键是DP状态的设计。
首先先判一下无解。显然,如果从起点出发能够走到一个走不到终点的点,则为无解。这很好想——只要答案有为无穷大的可能,无论概率多小,最终答案都会为无穷大。
然后就是DP设计了。我们无论设什么从起点出发的状态都是不太好的——所以,我们应该反过来,设从终点出发的状态。即,设\(f_x\)表示从节点\(x\)出发到终点的期望长度,则有
\[f_x=\dfrac{\sum\limits_{(x,y)\in\mathbb{E}}f_y}{deg_x}+1
\]
因为我们发现在不同SCC间转移是有序的,就可以直接按照拓扑序DP;只有在同一个SCC内部才会出现环,于是有环就直接用高斯消元暴力消掉即可。
代码:
#include<bits/stdc++.h>
using namespace std;
int n,m,S,T,col[10100],in[10100],ord[10100],out[10100],sz[10100],c;
vector<int>ele[10100];
namespace SCC{
int dfn[10100],low[10100],tot;
stack<int>stk;
vector<int>v[10100],u[10100];
bool vis1[10100],vis2[10100];
void dfs1(int x){
vis1[x]=true;
for(auto y:u[x])if(!vis1[y])dfs1(y);
}
void dfs2(int x){
if(!vis1[x]){puts("INF");exit(0);}
vis2[x]=true;
for(auto y:v[x])if(!vis2[y])dfs2(y);
}
void Tarjan(int x){
dfn[x]=low[x]=++tot,stk.push(x);
for(auto y:v[x]){
if(!dfn[y])Tarjan(y),low[x]=min(low[x],low[y]);
else if(!col[y])low[x]=min(low[x],dfn[y]);
}
if(dfn[x]!=low[x])return;
c++;
int y;
do{y=stk.top(),col[y]=c,ord[y]=sz[c]++,ele[c].push_back(y),stk.pop();}while(y!=x);
}
}
namespace DAG{
double f[10100],g[110][110];
vector<int>v[10100],u[10100];
void Gauss(int x){
for(auto i:ele[x]){
if(i==T){g[ord[i]][ord[i]]=1,g[ord[i]][sz[x]]=0;continue;}
for(auto j:SCC::v[i])if(col[i]==col[j])g[ord[i]][ord[j]]+=1.0/out[i];else g[ord[i]][sz[x]]-=f[j]/out[i];
g[ord[i]][sz[x]]-=1;
g[ord[i]][ord[i]]+=-1;
}
for(int i=0;i<sz[x];i++){
int mx=i;
for(int j=i+1;j<sz[x];j++)if(abs(g[mx][i])<abs(g[j][i]))mx=j;
if(i!=mx)for(int j=i;j<=sz[x];j++)swap(g[mx][j],g[i][j]);
for(int j=0;j<sz[x];j++){
if(i==j)continue;
double tmp=g[j][i]/g[i][i];
for(int k=i;k<=sz[x];k++)g[j][k]-=tmp*g[i][k];
}
}
for(auto i:ele[x])f[i]=g[ord[i]][sz[x]]/g[ord[i]][ord[i]];
for(int i=0;i<sz[x];i++)for(int j=0;j<=sz[x];j++)g[i][j]=0;
}
queue<int>q;
void Topo(){
q.push(col[T]);
while(!q.empty()){
int x=q.front();q.pop();
Gauss(x);
for(auto y:v[x]){
in[y]--;
if(!in[y])q.push(y);
}
}
}
}
int main(){
scanf("%d%d%d%d",&n,&m,&S,&T);
for(int i=1,x,y;i<=m;i++)scanf("%d%d",&x,&y),SCC::v[x].push_back(y),SCC::u[y].push_back(x),out[x]++;
SCC::dfs1(T),SCC::dfs2(S);
for(int i=1;i<=n;i++)if(!SCC::dfn[i])SCC::Tarjan(i);
for(int i=1;i<=n;i++)for(auto j:SCC::v[i])if(col[i]!=col[j])DAG::v[col[j]].push_back(col[i]),in[col[i]]++;
DAG::Topo();
printf("%.3lf\n",DAG::f[S]);
return 0;
}