BZOJ.2707.[SDOI2012]走迷宫(期望 Tarjan 高斯消元)
生活随笔
收集整理的這篇文章主要介紹了
BZOJ.2707.[SDOI2012]走迷宫(期望 Tarjan 高斯消元)
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
題目鏈接
一個點到達終點的期望步數 \(E_i=\sum_{(i,j)\in G}\frac{E_j+1}{out[i]}\),\(out[i]\)為點\(i\)的出度。
那么對于一個DAG可以直接在反向圖上拓撲+DP求解。
于是對于環內高斯消元,縮點后拓撲+DP。
無解(無限步)的情況: 起點到不了終點;起點能夠走到一個環,且在這個環內無法走到終點(走不出去)。
ps:1.T連出的邊不能計算。
2.期望的計算式有個+1!
3.建反向邊!
4.重邊
注:
如果\(E_i\)表示從起點到點\(i\)的期望步數,那么起點可能多次到達點\(i\),\(E_i\)這個值就。。(可以就直接拿起點做例子?)
如果\(E_i\)表示到達終點的期望步數就沒有這個問題。
//21136kb 5168ms #include <cmath> #include <cstdio> #include <cctype> #include <cstring> #include <algorithm> #define gc() getchar() const int N=1e4+5,M=1e6+5;int n,m,S,T,Enum,H[N],to[M],nxt[M],_H[N],_to[M],_nxt[M],in[N],q[N]; int tot,bel[N],scc[N][103],num[N],sz[N],Index,dfn[N],low[N],sk[N],top; double A[105][105],E[N],out[N]; bool vis[N],vis_s[N],exist[N];inline int read() {int now=0;register char c=gc();for(;!isdigit(c);c=gc());for(;isdigit(c);now=now*10+c-'0',c=gc());return now; } inline void AddEdge(int u,int v){to[++Enum]=v, nxt[Enum]=H[u], H[u]=Enum;_to[Enum]=u, _nxt[Enum]=_H[v], _H[v]=Enum; } void Tarjan(int x) {dfn[x]=low[x]=++Index, sk[++top]=x, exist[x]=1;for(int i=H[x]; i; i=nxt[i])if(!dfn[to[i]]) Tarjan(to[i]),low[x]=std::min(low[x],low[to[i]]);else if(exist[to[i]]) low[x]=std::min(low[x],dfn[to[i]]);if(dfn[x]==low[x]){++tot;do{bel[sk[top]]=tot, num[sk[top]]=sz[tot],scc[tot][sz[tot]++]=sk[top], exist[sk[top--]]=0;}while(sk[top+1]!=x);} } void DFS(int x) {vis[x]=vis_s[bel[x]]=1;if(x==T) return;//有沒有都行 for(int i=H[x]; i; i=nxt[i])if(!vis[to[i]]) /*++in[bel[x]],//Wrong*/DFS(to[i]); } void Gauss(int n) {for(int j=0; j<n; ++j){int mxrow=j;for(int i=j+1; i<n; ++i)if(fabs(A[i][j])>fabs(A[mxrow][j])) mxrow=i;if(mxrow!=j) for(int k=0; k<=n; ++k) std::swap(A[mxrow][k],A[j][k]);for(int i=j+1; i<n; ++i)if(A[i][j]){double t=A[i][j]/A[j][j];for(int k=j; k<=n; ++k)A[i][k]-=A[j][k]*t;}}for(int i=n-1; ~i; --i){for(int j=i+1; j<n; ++j) A[i][n]-=A[i][j]*A[j][n];A[i][n]/=A[i][i];} }int main() {n=read(),m=read(),S=read(),T=read();for(int u,v,i=1; i<=m; ++i) u=read(),v=read(),out[u]+=1.0,AddEdge(u,v);for(int i=1; i<=n; ++i)if(!dfn[i]) Tarjan(i);DFS(S);if(!vis[T]) {puts("INF"); return 0;}for(int x=1; x<=n; ++x)for(int i=H[x]; i; i=nxt[i])if(bel[x]!=bel[to[i]]) ++in[bel[x]];//反向圖上的入度+1。for(int i=1; i<=tot; ++i)if(vis_s[i]&&!in[i]&&bel[T]!=i) {puts("INF"); return 0;}for(int i=1; i<=n; ++i) out[i]=1.0/out[i];int h=0,t=0;q[t++]=bel[T]; // for(int i=1; i<=tot; ++i) // if(!in[i]) q[t++]=i;//in[]=0的只能是bel[T].while(h<t){int now=q[h++];memset(A,0,sizeof A);for(int j=0; j<sz[now]; ++j){int x=scc[now][j];A[j][j]=1.0, A[j][sz[now]]=E[x]/*之前加上的*/;if(x==T) continue;//不計算終點連出的邊!for(int i=H[x]; i; i=nxt[i])if(bel[to[i]]==now){A[j][sz[now]]+=out[x],//步數+1.A[j][num[to[i]]]-=out[x];//是點的出度不是in[]! //-=不能直接賦值=:有重邊!}}Gauss(sz[now]);for(int j=0; j<sz[now]; ++j){int x=scc[now][j];E[x]=A[j][sz[now]];for(int i=_H[x]; i; i=_nxt[i])if(bel[_to[i]]!=now){if(!--in[bel[_to[i]]]) q[t++]=bel[_to[i]];E[_to[i]]+=(E[x]+1)*out[_to[i]];}}}printf("%.3lf",E[S]);return 0; }
轉載于:https://www.cnblogs.com/SovietPower/p/8681877.html
總結
以上是生活随笔為你收集整理的BZOJ.2707.[SDOI2012]走迷宫(期望 Tarjan 高斯消元)的全部內容,希望文章能夠幫你解決所遇到的問題。