前言
吐了,这东西能出一万个锅。
题目
讲解
随机游走考虑高斯消元,结合强连通分量大小不超过 (100) 可以推测是对强连通分量进行高斯消元。
先考虑无解情况:从 (S) 走到一个无法到达 (T) 的点。
其实并不好做,稍微转化一下:如果存在一个 (S) 可以不经过 (T) 到达的点,但从 (T) 出发在反图上无法到达,即无解。
然后我们缩点,按拓扑序倒序做高斯消元,这样可以保证一个强连通分量连到外面的点的期望已经求出。
而这个拓扑序倒序其实就是我们求出强连通分量的顺序,所以直接做即可。
时间复杂度 (O(100^3(frac{n}{100})+m)=O(10^4n+m)),上限是 (10^8),可以过。
代码
自认为比较优美的代码
//12252024832524
#include <bits/stdc++.h>
#define TT template<typename T>
using namespace std;
typedef long long LL;
const int MAXN = 10005;
const int MAXM = 1000005;
int n,m,S,T;
int ID[MAXN];
LL Read()
{
LL x = 0,f = 1; char c = getchar();
while(c > '9' || c < '0'){if(c == '-') f = -1;c = getchar();}
while(c >= '0' && c <= '9'){x = (x*10) + (c^48);c = getchar();}
return x * f;
}
TT void Put1(T x)
{
if(x > 9) Put1(x/10);
putchar(x%10^48);
}
TT void Put(T x,char c = -1)
{
if(x < 0) putchar('-'),x = -x;
Put1(x); if(c >= 0) putchar(c);
}
TT T Max(T x,T y){return x > y ? x : y;}
TT T Min(T x,T y){return x < y ? x : y;}
TT T Abs(T x){return x < 0 ? -x : x;}
int head[MAXN],tot,rtot,rhead[MAXN];
struct edge
{
int v,nxt;
}e[MAXM],re[MAXM];
void Add_Edge(int &t,edge *E,int *hd,int x,int y)
{
E[++t] = edge{y,hd[x]};
hd[x] = t;
}
bool xvis[MAXN];
void xinei(int x)
{
xvis[x] = 1;
if(x == T) return;
for(int i = head[x]; i ;i = e[i].nxt) if(!xvis[e[i].v]) xinei(e[i].v);
}
bool nvis[MAXN];
void neixi(int x)
{
nvis[x] = 1;
for(int i = rhead[x]; i ;i = re[i].nxt) if(!nvis[re[i].v]) neixi(re[i].v);
}
bool ins[MAXN];
int s[MAXN],tl,dfn[MAXN],low[MAXN],dfntot,scc,bl[MAXN],deg[MAXN];
vector<int> p[MAXN];
void Tarjan(int x)
{
dfn[x] = low[x] = ++dfntot;
s[++tl] = x; ins[x] = 1;
for(int i = head[x],v; i ;i = e[i].nxt)
{
v = e[i].v;
if(!dfn[v]) Tarjan(v),low[x] = Min(low[x],low[v]);
else if(ins[v]) low[x] = Min(low[x],dfn[v]);
}
if(low[x] == dfn[x])
{
int v; ++scc;
do
{
v = s[tl--];
ins[v] = 0;
bl[v] = scc;
p[scc].emplace_back(v);
}while(v^x);
}
}
double a[105][105],dp[MAXN];
void Gauss(int N)
{
for(int i = 1;i <= N;++ i)
{
int now = i;
for(int j = i+1;j <= N;++ j) if(Abs(a[j][i]) > Abs(a[now][i])) now = j;
if(i ^ now) swap(a[i],a[now]);
for(int j = 1;j <= N;++ j)
{
if(i == j) continue;
double mu = a[j][i] / a[i][i];
for(int k = i;k <= N+1;++ k) a[j][k] -= mu * a[i][k];
}
}
}
int main()
{
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
n = Read(); m = Read(); S = Read(); T = Read();
for(int i = 1,u,v;i <= m;++ i) //之前以为是加边的问题,所以手动实现了函数Add_Edge
{
u = Read(),v = Read();
e[++tot] = edge{v,head[u]}; head[u] = tot;
re[++rtot] = edge{u,rhead[v]}; rhead[v] = rtot;
++deg[u];
}
xinei(S); neixi(T);
for(int i = 1;i <= n;++ i) if(xvis[i] && !nvis[i]) {printf("INF
");return 0;}
Tarjan(S);
for(int sc = 1;sc <= scc;++ sc)
{
int N = 0;
for(auto x : p[sc]) ID[x] = ++N,a[N][N] = 1;
for(auto x : p[sc])
{
if(x^T) a[ID[x]][N+1] = 1;
else continue;
for(int i = head[x],v; i ;i = e[i].nxt)
{
v = e[i].v;
if(bl[v] ^ bl[x]) a[ID[x]][N+1] += 1.0 / deg[x] * dp[v];
else a[ID[x]][ID[v]] += -1.0 / deg[x];
}
}
Gauss(N);
for(int i = 1;i <= N;++ i) dp[p[sc][i-1]] = a[i][N+1] / a[i][i];
for(int i = 1;i <= N;++ i)
for(int j = 1;j <= N+1;++ j)
a[i][j] = 0;
}
printf("%.3f
",dp[S]);
return 0;
}