[BZOJ2707]走迷宫
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"。
Sample Input
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
Sample Output
9.500
HINT
n<=10000,m<=1000000,保证每个强连通分量大小不超过100
题解
本题真的是无法描述啊......
这个高斯消元搞了我一晚上
正解其实想出来了但是就是不会打
既然题目都提示强连通分量了,肯定要跑个tarjan缩点呀
但是,经过一个强连通分量不一定就会INF,因为毕竟一次走不出去还有机会再次走出去
容易发现,INF的条件是“起点终点不连通”或“起点可以到达某个点,该点却不能到达终点”
上述INF情况可以跑正向+逆向dfs来判断
在不INF时,对于每个点,设f[i]为从i点到终点的期望步数,设du[i]为i点出度,f的方程容易想出来:
f[i]=1+sigma(f[j],i与j有边联通)/du[i]
(除了终点T,到了终点之后就不用再走了,因此f[T]=0,终点的出边也可以直接删除)
每个点的f长得和方程一样,那么我们就考虑解方程组:高斯消元
可我们发现数据范围太大(n<=10000)没办法消元
我们发现,题目提示强连通分量大小不超过100,100的范围是可以做的
因此我们考虑按照拓扑序逆序对每一个强连通分量进行高斯消元;
如果按照拓扑序逆序的话,计算某个连通块时,他会涉及到的出点都已经计算完了,所以这样是正确的
代码见下
1 #include<cstdio> 2 #include<cstring> 3 #include<queue> 4 #include<cmath> 5 #include<algorithm> 6 #include<vector> 7 using namespace std; 8 const int N=10100; 9 const int M=1000100; 10 struct node{int qi,zhong,next;}s[M],z[M],t[M]; 11 bool vis[N],t_vis[N],z_vis[N]; 12 int e,z_e,t_e,adj[N],z_adj[N],t_adj[N];//s最早的图,z缩点后正图,t缩点后反图 13 int n,m,S,T,id[N],chudu[N];//id为某个点在所属连通块 14 vector<int>man[N]; 15 int dfn[N],low[N],stack[N],top,num,belong[N],tot;//tarjan相关的数组 16 double A[210][210],f[N];//A为高斯消元,f为dp数组 17 inline void add(int qi,int zhong) 18 { 19 s[++e].zhong=zhong;s[e].qi=qi; 20 s[e].next=adj[qi];adj[qi]=e; 21 } 22 inline void z_add(int qi,int zhong) 23 { 24 z[++z_e].zhong=zhong;z[z_e].qi=qi; 25 z[z_e].next=z_adj[qi];z_adj[qi]=z_e; 26 } 27 inline void t_add(int qi,int zhong) 28 { 29 t[++t_e].zhong=zhong;t[t_e].qi=qi; 30 t[t_e].next=t_adj[qi];t_adj[qi]=t_e; 31 } 32 void tarjan(int rt) 33 { 34 dfn[rt]=low[rt]=++num; 35 stack[++top]=rt;vis[rt]=1; 36 for(int i=adj[rt];i;i=s[i].next) 37 { 38 int u=s[i].zhong; 39 if(!dfn[u])tarjan(u),low[rt]=min(low[u],low[rt]); 40 else if(!id[u])low[rt]=min(dfn[u],low[rt]); 41 } 42 if(dfn[rt]==low[rt]) 43 { 44 int v,ge=0;tot++; 45 do 46 { 47 v=stack[top--];id[v]=++ge; 48 belong[v]=tot;man[tot].push_back(v); 49 } 50 while(v!=rt); 51 } 52 } 53 void dfs1(int rt) 54 { 55 z_vis[rt]=1; 56 for(int i=z_adj[rt];i;i=z[i].next) 57 if(!z_vis[z[i].zhong])dfs1(z[i].zhong); 58 } 59 void dfs2(int rt) 60 { 61 t_vis[rt]=1; 62 for(int i=t_adj[rt];i;i=t[i].next) 63 if(!t_vis[t[i].zhong])dfs2(t[i].zhong); 64 } 65 inline bool judge()//正反dfs判断INF 66 { 67 dfs1(belong[S]);dfs2(belong[T]); 68 for(int i=1;i<=n;i++) 69 if(z_vis[i]&&!t_vis[i]) 70 return 0; 71 return 1; 72 } 73 inline void gasse(int b)//高斯消元 74 { 75 76 if(b==belong[T]){f[T]=0;return;} 77 int size=man[b].size(); 78 memset(A,0,sizeof(A)); 79 for(int i=0;i<size;i++) 80 { 81 int p=man[b][i]; 82 A[i][size]=chudu[p];//我统一把出度给乘上去了,没有写分数的形式 83 for(int j=adj[p];j;j=s[j].next) 84 { 85 int u=s[j].zhong; 86 if(belong[u]==b)//统计连通块自己的系数 87 A[i][id[u]-1]--; 88 else if(u!=T)//统计之前的贡献 89 A[i][size]+=f[u]; 90 } 91 A[i][i]+=chudu[p]; 92 } 93 for(int i=0;i<size;i++) 94 { 95 int p=i; 96 for(int j=i+1;j<size;j++) 97 if(fabs(A[p][i])<fabs(A[j][i]))p=j; 98 if(p!=i) 99 for(int j=0;j<=size;j++) 100 swap(A[p][j],A[i][j]); 101 for(int j=i+1;j<size;j++) 102 { 103 double tmp=A[j][i]/A[i][i]; 104 for(int k=i;k<=size;k++) 105 A[j][k]-=tmp*A[i][k]; 106 } 107 } 108 for(int i=size-1;i>=0;i--) 109 { 110 for(int j=i+1;j<size;j++) 111 A[i][size]-=A[j][size]*A[i][j]; 112 A[i][size]/=A[i][i]; 113 } 114 for(int i=0;i<size;i++) 115 f[man[b][i]]=A[i][size]; 116 } 117 void solve(int rt) 118 { 119 for(int i=z_adj[rt];i;i=z[i].next) 120 if(!vis[z[i].zhong]) 121 solve(z[i].zhong); 122 vis[rt]=1; 123 gasse(rt); 124 } 125 int main() 126 { 127 scanf("%d%d%d%d",&n,&m,&S,&T);int a,b; 128 if(S==T){printf("0.000");return 0;} 129 for(int i=1;i<=m;i++) 130 { 131 scanf("%d%d",&a,&b); 132 if(a==T)continue; 133 chudu[a]++,add(a,b); 134 } 135 for(int i=1;i<=n;i++)if(!dfn[i])tarjan(i); 136 for(int i=1;i<=n;i++) 137 { 138 for(int j=adj[i];j;j=s[j].next) 139 { 140 int u=s[j].zhong; 141 if(belong[i]!=belong[u]) 142 { 143 z_add(belong[i],belong[u]); 144 t_add(belong[u],belong[i]); 145 } 146 } 147 } 148 if(judge()) 149 { 150 memset(vis,0,sizeof(vis)); 151 solve(belong[S]); 152 printf("%.3lf",f[S]); 153 } 154 else{printf("INF");return 0;} 155 }
Progress is not created by contented people.