[SDOI2012]走迷宫

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;
}

posted @ 2021-04-02 15:37  Troverld  阅读(64)  评论(0编辑  收藏  举报