bzoj 2707 [SDOI2012]走迷宫(SCC+高斯消元)
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%的数据,图中没有环,也没有自环
【思路】
节点u的期望为E(u)=ΣE(v)/deg(u)+1,移项变成deg(u)*E(u)-ΣE(v)=deg(u),而且题目中说到每一个scc中的点数不超过100。我们就可以先划一下scc,将多个节点方程联立,在每个scc中用高斯消元求每个结点的E,O(n^2)。然后再缩点后的图上dfs统计一下就可以啦。
需要注意应该把t的所有出边切掉,因为我们规定E(t)=0。还有就是如果一个scc中有连往scc外的边,我们把常数项加个E
因为我们切了几条边,所以判断得改一下,不能直接判断sccno[S]是否直接到达sccno[T],可以两个点都dfs一遍,如果有sccno[S]不可以到达但sccno[T]可以到达的则为INF,此时出现有一个scc不能到达sccno[T]的情况,于是可以在这个scc中转来转去,即inf。
【代码】
1 #include<stack> 2 #include<cmath> 3 #include<vector> 4 #include<cstdio> 5 #include<cstring> 6 #include<iostream> 7 #include<algorithm> 8 #define FOR(a,b,c) for(int a=(b);a<=(c);a++) 9 using namespace std; 10 11 const int N = 1e4+5; 12 13 vector<int> g[3][N]; 14 int n,m,S,T,vis[N],deg[N]; 15 double ans[N],mat[101][101]; 16 17 int pre[N],sccno[N],id[N],lowlink[N],dfsc,scccnt; 18 stack<int> st; vector<int> scc[N]; 19 20 void tarjan(int flag,int u) { 21 pre[u]=lowlink[u]=++dfsc; 22 st.push(u); deg[u]=(int)g[flag][u].size(); 23 for(int i=0;i<deg[u];i++) { 24 int v=g[flag][u][i]; 25 if(!pre[v]) { 26 tarjan(flag,v); 27 lowlink[u]=min(lowlink[u],lowlink[v]); 28 } 29 else if(!sccno[v]) 30 lowlink[u]=min(lowlink[u],pre[v]); 31 } 32 if(pre[u]==lowlink[u]) { 33 ++scccnt; 34 for(;;) { 35 int x=st.top(); st.pop(); 36 sccno[x]=scccnt; 37 scc[scccnt].push_back(x); 38 id[x]=(int)scc[scccnt].size()-1; 39 if(x==u) break; 40 } 41 } 42 } 43 44 void gause(int x) { 45 int n=(int)scc[x].size(),i,j,k,r; 46 for(i=0;i<n;i++) { 47 int u=scc[x][i]; 48 for(j=0;j<n;j++) mat[i][j]=0; 49 mat[i][n]=deg[u]; 50 for(j=0;j<g[0][u].size();j++) { 51 int v=g[0][u][j]; 52 if(sccno[v]==x) { 53 mat[i][id[v]]--; 54 } else { 55 mat[i][n]+=ans[v]; 56 } 57 } 58 mat[i][i]+=deg[u]; 59 } 60 for(i=0;i<n;i++) { 61 r=i; 62 for(j=i+1;j<n;j++) 63 if(fabs(mat[j][i])>fabs(mat[r][i])) r=j; 64 if(r!=i) for(j=0;j<=n;j++) swap(mat[i][j],mat[r][j]); 65 for(k=i+1;k<n;k++) { 66 double f=mat[k][i]/mat[i][i]; 67 for(j=i;j<=n;j++) mat[k][j]-=f*mat[i][j]; 68 } 69 } 70 for(i=n-1;i>=0;i--) { 71 for(j=i+1;j<n;j++) 72 mat[i][n]-=mat[j][n]*mat[i][j]; 73 mat[i][n]/=mat[i][i]; 74 } 75 for(i=0;i<n;i++) 76 ans[scc[x][i]]=mat[i][n]; 77 } 78 79 void solve(int u) { 80 if(u==sccno[T]) { 81 ans[T]=0; return ; 82 } 83 for(int i=0;i<g[1][u].size();i++) { 84 int v=g[1][u][i]; 85 if(!vis[v]) solve(v); 86 } 87 vis[u]=1; 88 gause(u); 89 } 90 91 int mark[3][N]; 92 void dfs(int flag,int u) { 93 mark[flag][u]=1; 94 for(int i=0;i<g[flag][u].size();i++) { 95 int v=g[flag][u][i]; 96 if(!mark[flag][v]) dfs(flag,v); 97 } 98 } 99 int main() { 100 scanf("%d%d%d%d",&n,&m,&S,&T); 101 S--,T--; 102 int u,v; 103 for(int i=0;i<m;i++) { 104 scanf("%d%d",&u,&v); 105 u-- , v--; 106 if(u==T) continue; 107 g[0][u].push_back(v); 108 } 109 for(int i=0;i<n;i++) 110 if(!pre[i]) tarjan(0,i); 111 for(int i=0;i<n;i++) { 112 for(int j=0;j<g[0][i].size();j++) { 113 int v=g[0][i][j]; 114 if(sccno[i]!=sccno[v]) { 115 g[1][sccno[i]].push_back(sccno[v]); 116 g[2][sccno[v]].push_back(sccno[i]); 117 } 118 } 119 } 120 dfs(1,sccno[S]); dfs(2,sccno[T]); 121 for(int i=1;i<=scccnt;i++) 122 if(mark[1][i]&&!mark[2][i]) { 123 puts("INF"); return 0; 124 } 125 solve(sccno[S]); 126 printf("%.3f",ans[S]); 127 return 0; 128 }
PS:
这个题=-= 好神啊0_0
其实第一眼蒟蒻是想dfs来着,但是有圈啊……有圈啊……有圈啊……
posted on 2016-02-29 20:31 hahalidaxin 阅读(433) 评论(0) 编辑 收藏 举报