[SDOI2012]走迷宫 强连通+概率DP+高斯消元
作者:互联网
题目
([SDOI2012]走迷宫) https://ac.nowcoder.com/acm/problem/20575
题目描述
Morenan被困在了一个迷宫里。迷宫可以视为N个点M条边的有向图,其中Morenan处于起点S,迷宫的终点设为T。可惜的是,Morenan非常的脑小,他只会从一个点出发随机沿着一条从该点出发的有向边,到达另一个点。这样,Morenan走的步数可能很长,也可能是无限,更可能到不了终点。若到不了终点,则步数视为无穷大。但你必须想方设法求出Morenan所走步数的期望值。
n<10000,m<1000000,每个SCC大小不超过100
输入描述:
第1行4个整数,N,M,S,T第[2, M+1]行每行两个整数o1, o2,表示有一条从o1到o2的边。
输出描述:
一个浮点数,保留小数点3位,为步数的期望值。若期望值为无穷大,则输出"INF"。
样例一
输入
6 6 1 6
1 2
1 3
2 4
3 5
4 6
5 6
输出
3.000
样例二
输入
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
输出
9.500
样例三
输入
2 0 1 2
输出
INF
思路
我们先考虑INF的情况。如果存在一个点X不能到达T,但是S能够到达X。那么就是是INF。我们把边取反。那么T能够
到达的点就是能够到达T的点,再DFS(S)一次。就可以判断INF。
然后。我们知道期望反推
但是存在一个问题,就是n太大,直接高斯消元会T。但是每个scc<=100,我们把图缩点。从T的scc开始。按拓扑反序进行一个一个scc的高斯消元。
对于同一个scc的边,按上面的公式就可以了,如果\(u->to。scc[u]!=scc[to]\),那么f[to]一定知道了。就是常数项\(+(f[to]+1)/G[u].sie()\)
F[T]=0, 那么F[s]就是答案。复杂度O(n^4)
坑点:重边,有自环
#include<bits/stdc++.h>
#define LL long long
using namespace std;
struct Edge {
int from, to, nxt;
} e[2000005];
int head[100005], cut=0, N=0;
int scc[100005];
vector<int> g[100005];
struct Scc {
int dfn[100005], low[100005], vis[100005], T=0;
stack<int> sk;
void Addedge(int x, int y) {
e[++cut]= {x, y, head[x]};
head[x]=cut;
}
void Ta(int u) {
dfn[u]=low[u]=++T;
sk.push(u);
vis[u]=1;
for(int i=head[u]; i; i=e[i].nxt) {
int to=e[i].to;
if(!dfn[to]) {
Ta(to);
low[u]=min(low[u], low[to]);
} else if(vis[to]) {
low[u]=min(low[u], dfn[to]);
}
}
if(low[u]==dfn[u]) {
N++;
while(1) {
int now=sk.top();
sk.pop();
vis[now]=0;
scc[now]=N;
g[N].push_back(now);
if(now==u) {
break;
}
}
}
}
} sc;
int d[200005];
vector<int> G[200005], Gf[200005];
const double eps=1e-5;
double a[205][205];
double ans[205];
map<int, int> mp;
void Gauss(int n) {
for(int i=1; i<=n; ++i) {
int r=i;
for(int j=i+1; j<=n; ++j) //找主元系数的绝对值最大的一行
if(fabs(a[j][i])>fabs(a[r][i]))
r=j;
if(fabs(a[r][i])<eps) { //最大为0,无解
printf("No Solution\n");
return;
}
if(r!=i)
for(int j=1; j<=n+1; ++j)
swap(a[i][j],a[r][j]);
for(int j=1; j<=n; ++j) //消元
if(j!=i) {
double tmp=a[j][i]/a[i][i];
for(int k=i; k<=n+1; ++k)
a[j][k]-=a[i][k]*tmp;
}
}
for(int i=1; i<=n; ++i)
ans[i]=a[i][n+1]/a[i][i];
}
queue<int> q;
int vis[200005];
vector<int> c;
void dfs(int u){
vis[u]=1;
for(auto x: Gf[u]){
if(!vis[x]){
dfs(x);
}
}
}
int dfsinf(int u, int t){
if(u==t) return 1;
if(vis[u]==0) return 0;
vis[u]++;
for(int i=head[u]; i; i=e[i].nxt){
int x=e[i].to;
if(vis[x]<2){
if(!dfsinf(x, t)) return 0;
}
}
return 1;
}
double f[100005];
void dfs(int u, int N) {//每个scc构建高斯消元
int sz=0;
vis[u]=1;
c.push_back(u);
for(int i=head[u]; i; i=e[i].nxt) {
sz++;
}
for(int i=head[u]; i; i=e[i].nxt) {
int to=e[i].to;
if(scc[to]==scc[u]) {
a[mp[u]][mp[to]]+=-1.0/sz;
if(!vis[to]) {
dfs(to, N);
}
}
}
a[mp[u]][mp[u]]+=1;
a[mp[u]][N+1]=1;
}
int main() {
int n, m, s, t;
scanf("%d%d%d%d", &n, &m, &s, &t);
for(int i=1; i <=m; i++) {
int x, y;
scanf("%d%d", &x, &y);
sc.Addedge(x, y);
Gf[y].push_back(x);
}
for(int i=1; i<=n; i++) {
if(!sc.dfn[i]) {
sc.Ta(i);
}
}
for(int i=1; i<=m; i++) {
int x=scc[e[i].from], y=scc[e[i].to];
if(x!=y) {
d[x]++;
G[y].push_back(x);
}
}
dfs(t);
if(!dfsinf(s, t)) {
printf("INF\n");
return 0;
}
memset(vis, 0, sizeof(vis));
q.push(scc[t]);
while(!q.empty()) {//反拓扑序dp
int now=q.front();
q.pop();
mp.clear();
int N=0;
for(auto x: g[now]) {
mp[x]=++N;
}
for(auto x: c){
vis[x]=0;
}
c.clear();
memset(a, 0, sizeof(a));
dfs(g[now][0], N);
for(auto x: g[now]) {
int sz=0;
for(int i=head[x]; i; i=e[i].nxt) {
sz++;
}
for(int i=head[x]; i; i=e[i].nxt) {
int y=e[i].to;
if(scc[x]!=scc[y]) {
a[mp[x]][N+1]+=(f[y])/sz;
}
}
}
if(now==scc[t]) {//f[t]=0
for(int i=1; i<=N+1; i++) {
a[mp[t]][i]=0;
}
a[mp[t]][mp[t]]=1;
}
Gauss(N);
for(auto x: g[now]) {
f[x]=ans[mp[x]];
}
for(auto x: G[now]) {
d[x]--;
if(d[x]==0) {
q.push(x);
}
}
}
printf("%.3f\n", fabs(f[s]));
return 0;
}
/*
8 12 2 4
3 6
4 1
6 8
3 1
2 6
5 4
5 2
8 1
7 3
1 4
2 6
8 7
*/
标签:int,scc,100005,vis,SDOI2012,高斯消,now,DP,low 来源: https://www.cnblogs.com/liweihang/p/13644282.html