【BZOJ2707】[SDOI2012]走迷宫
Description
Morenan被困在了一个迷宫里。迷宫可以视为N个点M条边的有向图,其中Morenan处于起点S,迷宫的终点设为T。可惜的是,Morenan非常的脑小,他只会从一个点出发随机沿着一条从该点出发的有向边,到达另一个点。这样,Morenan走的步数可能很长,也可能是无限,更可能到不了终点。若到不了终点,则步数视为无穷大。但你必须想方设法求出Morenan所走步数的期望值。
Input
第1行4个整数,N,M,S,T
第[2, M+1]行每行两个整数o1, o2,表示有一条从o1到o2的边。
Output
一个浮点数,保留小数点3位,为步数的期望值。若期望值为无穷大,则输出"INF"。
【样例输入1】
6 6 1 6
1 2
1 3
2 4
3 5
4 6
5 6
【样例输出1】
3.000
【样例输入2】
9 12 1 9
1 2
2 3
3 1
3 4
3 7
4 5
5 6
6 4
6 7
7 8
8 9
9 7
【样例输出2】
9.500
【样例输入3】
2 0 1 2
【样例输出3】
INF
【数据范围】
测试点
|
N
|
M
|
Hint
|
[1, 6]
|
<=10
|
<=100
|
|
[7, 12]
|
<=200
|
<=10000
|
|
[13, 20]
|
<=10000
|
<=1000000
|
保证强连通分量的大小不超过100
|
另外,均匀分布着40%的数据,图中没有环,也没有自环
题解:先Tarjan缩环,然后对于强连通分量内部的点,用高斯消元,外部的DAG用拓扑排序+DP。具体细节还是说一下吧:
1.高斯消元时,列方程$f[i]=sum f[j]/d[i]+1$,发现i的某些出边可能指向强连通分量外的点,把它们都当成常数项就好了。特别地,T的方程要特殊处理。
2.判断INF时比较坑,从S开始BFS,我们的DAG只能包含我们能DFS到的点,并且,搜到T时要立刻停止。DFS后,如果发现一个强连通分量的出度为0且不是T所在的强连通分量,则INF。
#include <cstdio> #include <cstring> #include <iostream> #include <vector> #include <queue> #include <cmath> const int maxn=10010; using namespace std; int n,m,cnt,Cnt,tot,top,sum,S,T; vector<int> s[maxn]; int dep[maxn],low[maxn],head[maxn],to[1000010],next[1000010],sta[maxn],d[maxn],ins[maxn],D[maxn],pos[maxn],bel[maxn]; int To[1000010],Next[1000010],Head[maxn],vis[maxn]; double p[maxn],v[110][110]; queue<int> q; inline void add(int a,int b) { to[cnt]=b,next[cnt]=head[a],head[a]=cnt++; } inline void Add(int a,int b) { To[Cnt]=b,Next[Cnt]=Head[a],Head[a]=Cnt++; } void tarjan(int x) { dep[x]=low[x]=++tot,sta[++top]=x,ins[x]=1; int i,t; for(i=Head[x];i!=-1;i=Next[i]) { if(!dep[To[i]]) tarjan(To[i]),low[x]=min(low[x],low[To[i]]); else if(ins[To[i]]) low[x]=min(low[x],dep[To[i]]); } if(dep[x]==low[x]) { sum++; do { t=sta[top--],ins[t]=0,bel[t]=sum,pos[t]=s[sum].size(),s[sum].push_back(t); }while(t!=x); } } void calc(int x) { int i,j,k,a,b,nm=s[x].size(); for(i=0;i<nm;i++) memset(v[i],0,sizeof(v[0][0])*(nm+1)); for(i=0;i<nm;i++) { a=s[x][i]; for(j=head[a];j!=-1;j=next[j]) { b=to[j]; if(bel[b]==bel[a]) v[pos[b]][pos[a]]+=1.0/d[b],v[pos[b]][nm]-=1.0/d[b]; } } for(i=0;i<nm;i++) { v[i][nm]-=p[s[x][i]]; if(s[x][i]==T) for(j=0;j<=nm;j++) v[i][j]=0; v[i][i]-=1; } for(i=0;i<nm;i++) { for(j=i+1;j<nm;j++) if(fabs(v[j][i])>fabs(v[i][i])) for(k=i;k<=nm;k++) swap(v[j][k],v[i][k]); double tmp=v[i][i]; if(fabs(tmp)<1e-9) continue; for(k=i;k<=nm;k++) v[i][k]/=tmp; for(j=0;j<nm;j++) if(i!=j) { tmp=v[j][i]; for(k=i;k<=nm;k++) v[j][k]-=v[i][k]*tmp; } } for(i=0;i<nm;i++) p[s[x][i]]=v[i][nm]; } void dfs(int x) { vis[x]=1; if(x==T) return ; for(int i=Head[x];i!=-1;i=Next[i]) { if(bel[To[i]]!=bel[x]) D[bel[x]]++; if(!vis[To[i]]) dfs(To[i]); } } inline int rd() { int ret=0,f=1; char gc=getchar(); while(gc<'0'||gc>'9') {if(gc=='-')f=-f; gc=getchar();} while(gc>='0'&&gc<='9') ret=ret*10+gc-'0',gc=getchar(); return ret*f; } int main() { n=rd(),m=rd(),S=rd(),T=rd(); int i,j,a,b,u,v; memset(head,-1,sizeof(head)),memset(Head,-1,sizeof(Head)); for(i=1;i<=m;i++) a=rd(),b=rd(),Add(a,b),add(b,a),d[a]++; for(i=1;i<=n;i++) if(!dep[i]) tarjan(i); dfs(S); for(i=1;i<=n;i++) if(vis[i]&&bel[i]!=bel[T]&&!D[bel[i]]) { printf("INF"); return 0; } q.push(bel[T]); while(!q.empty()) { u=q.front(),q.pop(); calc(u); if(u==bel[S]) { printf("%.3lf",fabs(p[S])); return 0; } for(i=0;i<(int)s[u].size();i++) for(v=s[u][i],j=head[v];j!=-1;j=next[j]) if(bel[to[j]]!=bel[v]) { D[bel[to[j]]]--,p[to[j]]+=(p[v]+1)/d[to[j]]; if(!D[bel[to[j]]]) q.push(bel[to[j]]); } } printf("INF"); return 0; }//9 12 1 6 1 2 2 3 3 1 3 4 3 7 4 5 5 6 6 4 6 7 7 8 8 9 9 7