標籤:from pac main tor const else graph 思路 name
題意:給你一個有向圖,點數10000,邊數1000000,SCC大小不超過100(按資料範圍的寫法只有第三部分資料滿足這個條件,不過第二部分資料並沒有出現大小大於100個點的SCC,我是用數組大小為100的代碼以身試法的2333)從s出發隨機走,問走到t的期望步數.
首先考慮inf的情況.如果從s出發可以走到一個無法走到t的點,比如這個資料:紅色點為起點,綠色點為終點,那麼有1/2的機率永遠也走不到(在藍色點停下).
注意出現環的情況不一定是INF,因為在環上走無窮步的機率可能是無窮小。於是先縮點,把邊反向找到所有不能到達t的SCC,如果從s出發有可能到達這樣的一個SCC或s本身處於這樣一個SCC,那麼答案是INF。
接下來,我們把期望步數轉化成期望經過的點數(顯然經過的邊數等於點數-1),那麼利用期望的線性性,只需要高斯消元求出每個點的期望經過次數再加起來。但是這個範圍顯然不能直接做。而SCC大小小於100,提醒我們可以對每個SCC分別進行高斯消元,然後考慮SCC之間的關係。思路類似USACO一道最短路題”道路與航線”,那道題是對每個SCC分別跑dijkstra。
具體的做法:記f[i]為點i的期望經過次數,g[i]為從另一個SCC走到點i的期望次數,因為我們按拓撲序處理每個SCC,所以在處理每個SCC的時候這個SCC中每個點的g[]值都已經求出來了.接下來對SCC中每個點列一個方程.對於點x,f[x]=g[x]+sigma(f[j]/outdeg[j]),j向x有一條有向邊且j和x在同一個SCC,outdeg為出度。這裡j可以等於x(有自環),驗證一下,這時候方程也是對的.解完這個SCC之後要用這個SCC裡的點更新其他SCC的g[].注意邊界g[s]=1,f[t]=1
然後碼碼碼就好了。
#include<cstdio>#include<vector>#include<algorithm>using namespace std;int n,m,s,t;const int maxn=10005,maxm=1000005;//Graph Theorystruct edge{ int to,next;}lst1[maxm],lst2[maxm],lst3[maxm];int len1=1,len2=1,len3=1;int first1[maxn],first2[maxn],first3[maxn];void addedge1(int a,int b){ lst1[len1].to=b;lst1[len1].next=first1[a]; first1[a]=len1++;}void addedge2(int a,int b){ lst2[len2].to=b;lst2[len2].next=first2[a]; first2[a]=len2++;}void addedge3(int a,int b){ lst3[len3].to=b;lst3[len3].next=first3[a]; first3[a]=len3++;}int outdeg[maxn];int belong[maxn],tot,sz[maxn];vector<int> scc[maxn];int stk[maxn],top,dfn[maxn],low[maxn],T;bool ins[maxn];namespace Trajan{ void dfs(int x){ low[x]=dfn[x]=++T; stk[top++]=x;ins[x]=true; for(int pt=first1[x];pt;pt=lst1[pt].next){ if(!dfn[lst1[pt].to]){ dfs(lst1[pt].to); if(low[lst1[pt].to]<low[x])low[x]=low[lst1[pt].to]; }else if(ins[lst1[pt].to]&&dfn[lst1[pt].to]<low[x])low[x]=dfn[lst1[pt].to]; } if(dfn[x]==low[x]){ ++tot; do{ ins[stk[--top]]=false; belong[stk[top]]=tot; scc[tot].push_back(stk[top]); sz[tot]++; }while(stk[top]!=x); } } void tarjan(){ for(int i=1;i<=n;++i){ if(!dfn[i])dfs(i); } for(int i=1;i<=n;++i){ for(int pt=first1[i];pt;pt=lst1[pt].next){ if(belong[lst1[pt].to]!=belong[i]){ addedge2(belong[lst1[pt].to],belong[i]); addedge3(belong[i],belong[lst1[pt].to]); } } } } bool reachfromend[maxn],mustreachend[maxn]; void predfs(int x){ reachfromend[x]=true; for(int pt=first2[x];pt;pt=lst2[pt].next){ if(!reachfromend[lst2[pt].to]){ predfs(lst2[pt].to); } } } bool checkdfs(int x){ if(!reachfromend[x])return false; for(int pt=first3[x];pt;pt=lst3[pt].next){ if(mustreachend[lst3[pt].to])continue; if(!checkdfs(lst3[pt].to))return false; } return mustreachend[x]=true; } bool check(){ predfs(belong[t]); return checkdfs(belong[s]); }};double f[maxn],g[maxn];int map[maxn];namespace Work{ bool done[maxn]; double F[105][105]; int rk; void Swap(int a,int b){ for(int i=0;i<=rk;++i)swap(F[a][i],F[b][i]); } void multplus(int a,int b,double times){ for(int i=0;i<=rk;++i)F[a][i]+=F[b][i]*times; } void Gauss(int n){ rk=n; for(int i=1;i<=n;++i){ if(F[i][i]==0){ for(int j=i+1;j<=n;++j){ if(F[j][i]!=0){ Swap(i,j);break; } } } for(int j=i+1;j<=n;++j)multplus(j,i,-F[j][i]/F[i][i]); } for(int i=n;i>=1;--i){ F[i][0]/=F[i][i]; for(int j=i-1;j>=1;--j){ F[j][0]-=F[j][i]*F[i][0]; } } } void build_equations(int x){ for(int i=1;i<=sz[x];++i){ for(int j=0;j<=sz[x];++j){ F[i][j]=0; } } for(int i=0;i<sz[x];++i)F[i+1][0]=-g[scc[x][i]]; for(int i=1;i<=sz[x];++i){ F[i][i]=-1;map[scc[x][i-1]]=i; if(scc[x][i-1]==t)F[i][0]=-1; } for(int i=0;i<sz[x];++i){ if(scc[x][i]==t)continue; for(int pt=first1[scc[x][i]];pt;pt=lst1[pt].next){ if(belong[lst1[pt].to]==x){ if(lst1[pt].to==t)continue; F[map[lst1[pt].to]][i+1]+=1.0/outdeg[scc[x][i]]; } } } } void dfs(int x){ for(int pt=first2[x];pt;pt=lst2[pt].next){ if(!done[lst2[pt].to])dfs(lst2[pt].to); } build_equations(x); Gauss(sz[x]); for(int i=0;i<sz[x];++i){ f[scc[x][i]]=F[i+1][0]; } for(int i=0;i<sz[x];++i){ for(int pt=first1[scc[x][i]];pt;pt=lst1[pt].next){ if(belong[lst1[pt].to]!=x){ g[lst1[pt].to]+=f[scc[x][i]]/outdeg[scc[x][i]]; } } } done[x]=true; }}int main(){ scanf("%d%d%d%d",&n,&m,&s,&t); int a,b; for(int i=1;i<=m;++i){ scanf("%d%d",&a,&b);addedge1(a,b); outdeg[a]++; } Trajan::tarjan(); if(Trajan::check()){ g[s]=1; Work::dfs(belong[t]); double ans=0; for(int i=1;i<=n;++i){//printf("%.2f ",f[i]); ans+=f[i]; }//ans:the expected number of points on the path from s to t printf("%.3f\n",ans-1); }else{ printf("INF\n"); } return 0;}
bzoj2702[SDOI2012]走迷宮