题解 P6030 [SDOI2012]走迷宫
题面传送门
之所以写个题解是因为题解区大部分题解的做法都有 bug(u1s1 周六上午在讨论区里连发两个 hack 的是我,由于我被禁言才让 ycx 代发的)
首先碰到这种期望题,我们套路地设
等等……
注意到题目中有个条件,就是每个强连通分量大小 tarjan,因为最终强连通分量的编号本身就是按拓扑序倒序编好号的了,就 duck 不必再写遍拓扑排序了,直接从
记
那么什么情况输出 INF 呢?显然如果 INF,接下来就是我要强调的地方了,不少题解都认为,只要存在 INF,但考虑下面的数据:
3 2 1 3
1 3
3 2
事实上,
还有的题解稍微明智些,把不能到达的点的
5 5 1 5
1 5
1 2
2 3
3 4
4 2
我的做法是,先建反图,以
当然我的做法可能也存在漏洞(只是我发现不了了),如果发现漏洞请及时提出,谢谢。
我认为做题还是要严谨些,愿管理员把 hack 数据加入本题的测试数据中。
那问题就来了,为什么我就不能把我 hack 的这份热情放到 CF 比赛中呢
const int MAXS=100;
const int MAXN=10000;
const int MAXM=1e6;
const double INF=1e15;
int n,m,s,t;
int hd[MAXN+5],to[MAXM*2+5],nxt[MAXM*2+5],ec=0;
void adde(int u,int v){to[++ec]=v;nxt[ec]=hd[u];hd[u]=ec;}
int bel[MAXN+5],cmp=0,dfn[MAXN+5],low[MAXN+5],tim=0;
bool vis[MAXN+5];int stk[MAXN+5],top=0;vector<int> scc[MAXN+5];
vector<int> rev[MAXN+5];bool can[MAXN+5];
void dfs(int x){
if(can[x]) return;can[x]=1;
for(int y:rev[x]) dfs(y);
}
void tarjan(int x){
dfn[x]=low[x]=++tim;vis[x]=1;stk[++top]=x;
for(int e=hd[x];e;e=nxt[e]){
int y=to[e];
if(!dfn[y]) tarjan(y),chkmin(low[x],low[y]);
else if(vis[y]) chkmin(low[x],dfn[y]);
}
if(low[x]==dfn[x]){
cmp++;int o;
do {
o=stk[top--];vis[o]=0;
scc[bel[o]=cmp].pb(o);
} while(o!=x);
}
}
int id[MAXN+5],seq[MAXS+5],subsiz=0,deg[MAXN+5];
double dp[MAXN+5],a[MAXS+5][MAXS+5],f[MAXS+5];
int main(){
scanf("%d%d%d%d",&n,&m,&s,&t);
for(int i=1,u,v;i<=m;i++){
scanf("%d%d",&u,&v);deg[u]++;
adde(u,v);rev[v].pb(u);
}
tarjan(s);dfs(t);if(!dfn[t]) return puts("INF"),0;
for(int i=1;i<=cmp;i++){
subsiz=0;memset(a,0,sizeof(a));memset(f,0,sizeof(f));
for(int u:scc[i]){seq[++subsiz]=u;id[u]=subsiz;}
for(int u:scc[i]){
int p=id[u];
if(u==t){a[p][p]=1;continue;}
a[p][p]=a[p][subsiz+1]=deg[u];
for(int e=hd[u];e;e=nxt[e]){
int v=to[e];
if(bel[v]==bel[u]) a[p][id[v]]--;
else a[p][subsiz+1]+=dp[v];
}
if(!can[u]) a[p][subsiz+1]=INF;
}
// for(int j=1;j<=subsiz;j++) for(int k=1;k<=subsiz+1;k++)
// printf("%.3lf%c",a[j][k],(k==subsiz+1)?'\n':' ');
for(int j=1;j<=subsiz;j++){
int t=j;
for(int k=j+1;k<=subsiz;k++) if(fabs(a[k][j])>fabs(a[t][j])) t=k;
for(int k=j;k<=subsiz+1;k++) swap(a[t][k],a[j][k]);
for(int k=j+1;k<=subsiz+1;k++) a[j][k]/=a[j][j];a[j][j]=1;
for(int k=j+1;k<=subsiz;k++){
for(int l=j+1;l<=subsiz+1;l++) a[k][l]-=a[k][j]*a[j][l];
a[k][j]=0;
}
}
for(int j=subsiz;j;j--){
f[j]=a[j][subsiz+1];
for(int k=j+1;k<=subsiz;k++) f[j]-=f[k]*a[j][k];
// printf("%.3lf\n",f[j]);
}
for(int j=1;j<=subsiz;j++){
if(f[j]>1e9) dp[seq[j]]=INF;
else dp[seq[j]]=f[j];
}
}
// for(int i=1;i<=n;i++) cout<<dp[i]<<endl;
if(dp[s]>1e9) puts("INF");
else printf("%.3lf\n",dp[s]);
return 0;
}