【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];
}
posted @ 2018-05-25 13:48  yoyoball  阅读(123)  评论(0编辑  收藏  举报