洛谷 P6030 - [SDOI2012]走迷宫(高斯消元+SCC 缩点)
之所以写个题解是因为题解区大部分题解的做法都有 bug(u1s1 周六上午在讨论区里连发两个 hack 的是我,由于我被禁言才让 ycx 代发的)
首先碰到这种期望题,我们套路地设 \(dp_u\) 为从节点 \(u\) 走到节点 \(n\) 经过的节点数的期望值,那么显然有转移方程 \(dp_u=\dfrac{1}{deg_u}(\sum\limits_{(u,v)\in E}dp_v)+1\),由于这个 \(dp\) 方程存在环,故需按照 P3232 游走 的套路进行高斯消元,具体来说你将这 \(n\) 个 \(dp\) 转移式写成矩阵的形式高斯消元一下即可。
等等……\(10^4\) 你让我跑高斯消元?
注意到题目中有个条件,就是每个强连通分量大小 \(\le 100\),因此考虑先将原图进行一遍 SCC 缩点,缩点完成后显然原图变成了一个 DAG,我们考虑按这个 DAG 的拓扑序倒序(或者说,以 \(t\) 为起点拓扑排序)对每个强连通分量中的点计算一遍 \(dp\) 值,具体来说我们给当前强连通分量中的所有点重新编号,对于形如 \(dp_u=\dfrac{1}{deg_u}(\sum\limits_{(u,v)\in E}dp_v)+1\) 的式子,如果 \(v\) 与 \(u\) 在同一个强连通分量中那就按照套路将式子改写成一个 \(dp_u\) 与这样的 \(dp_v\) 的关系式,否则由于我们按照拓扑序倒序计算答案,\(dp_v\) 的值肯定已经计算好了,那么我们把它当作常数项拖到右边去即可。具体实现的时候可以以 \(s\) 为起点跑一遍 tarjan,因为最终强连通分量的编号本身就是按拓扑序倒序编好号的了,就 duck 不必再写遍拓扑排序了,直接从 \(1\) 枚举到 \(\text{强连通分量个数}\) 依次计算即可。
记 \(s_i\) 为强连通分量大小,那么该算法复杂度 \(T(n)=\sum\limits_{i=1}^ms_i^3\),而 \(s_i\le 100\),故 \(T(n)\le\dfrac{10^4}{100}\times 100^3=10^8\),可以通过此题。
那么什么情况输出 INF 呢?显然如果 \(s\) 不能到 \(t\) 答案肯定是 INF,接下来就是我要强调的地方了,不少题解都认为,只要存在 \(s\) 能到达但却不能到达 \(t\) 的点就 INF,但考虑下面的数据:
3 2 1 3
1 3
3 2
事实上,\(2\) 虽然能够从 \(3\) 到达,但从 \(1\to 2\) 的路径上已经经过 \(3\) 了,因此是可以到达 \(2\) 的,答案应当为 \(1.000\)。
还有的题解稍微明智些,把不能到达的点的 \(dp\) 值设为 \(\infty\),然后判是否有 \(dp_s=\infty\),这样做是可以避免掉上述情况的,但由于实现上出了个小 bug(如果一个点出度为 \(0\) 那么它的 \(dp\) 值就是 \(\infty\)),导致其可以被以下的数据叉掉:
5 5 1 5
1 5
1 2
2 3
3 4
4 2
我的做法是,先建反图,以 \(t\) 为起点做一遍 DFS 找出所有能到达 \(t\) 的点,然后如果一个点不能到达 \(t\) 那就令它的 \(dp\) 值为 \(\infty\),这样又可避免上述情况。
当然我的做法可能也存在漏洞(只是我发现不了了),如果发现漏洞请及时提出,谢谢。
我认为做题还是要严谨些,愿管理员把 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;
}
洛谷 P6030 - [SDOI2012]走迷宫(高斯消元+SCC 缩点)的更多相关文章
- BZOJ 2707: [SDOI2012]走迷宫 [高斯消元 scc缩点]
2707: [SDOI2012]走迷宫 题意:求s走到t期望步数,\(n \le 10^4\),保证\(|SCC| \le 100\) 求scc缩点,每个scc高斯消元,scc之间直接DP 注意每次清 ...
- l洛谷 P6030 [SDOI2012]走迷宫 概率与期望+高斯消元
题目描述 传送门 分析 首先判掉 \(INF\) 的情况 第一种情况就是不能从 \(s\) 走到 \(t\) 第二种情况就是从 \(s\) 出发走到了出度为 \(0\) 的点,这样就再也走不到 \(t ...
- 洛谷P2973 [USACO10HOL]赶小猪(高斯消元 期望)
题意 题目链接 Sol 设\(f[i]\)表示炸弹到达\(i\)这个点的概率,转移的时候考虑从哪个点转移而来 \(f[i] = \sum_{\frac{f(j) * (1 - \frac{p}{q}) ...
- 洛谷3317 SDOI2014重建(高斯消元+期望)
qwq 一开始想了个错的做法. 哎 直接开始说比较正确的做法吧. 首先我们考虑题目的\(ans\)该怎么去求 我们令\(x\)表示原图中的某一条边 \[ans = \sum \prod_{x\in t ...
- 【BZOJ3143】游走(高斯消元,数学期望)
[BZOJ3143]游走(高斯消元,数学期望) 题面 BZOJ 题解 首先,概率不会直接算... 所以来一个逼近法算概率 这样就可以求出每一条边的概率 随着走的步数的增多,答案越接近 (我卡到\(50 ...
- 洛谷P3232 [HNOI2013]游走(高斯消元+期望)
传送门 所以说我讨厌数学……期望不会高斯消元也不会……好不容易抄好了高斯消元板子被精度卡成琪露诺了…… 首先,我们先算出走每一条边的期望次数,那么为了最小化期望,就让大的期望次数乘上小编号 边的期望次 ...
- [HNOI2013]游走 期望+高斯消元
纪念首道期望题(虽说绿豆蛙的归宿才是,但是我打的深搜总觉得不正规). 我们求出每条边的期望经过次数,然后排序,经过多的序号小,经过少的序号大,这样就可以保证最后的值最小. 对于每一条边的期望经过次数, ...
- LOJ 2542 「PKUWC2018」随机游走 ——树上高斯消元(期望DP)+最值反演+fmt
题目:https://loj.ac/problem/2542 可以最值反演.注意 min 不是独立地算从根走到每个点的最小值,在点集里取 min ,而是整体来看,“从根开始走到点集中的任意一个点就停下 ...
- 【BZOJ】3143: [Hnoi2013]游走 期望+高斯消元
[题意]给定n个点m条边的无向连通图,每条路径的代价是其编号大小,每个点等概率往周围走,要求给所有边编号,使得从1到n的期望总分最小(求该总分).n<=500. [算法]期望+高斯消元 [题解] ...
随机推荐
- java---String 和 StringBuffer
Java-String和StringBuffer类 Java String 类 字符串在Java中属于对象,Java提供String类来创建和操作字符串. 创建字符串 创建字符串常用的方法如下: ...
- vue2和vue3比较
一.vue3新特性: 1.数据响应重新实现(ES6的proxy代替Es5的Object.defineProperty) 2.源码使用ts重写,更好的类型推导 3.虚拟DOM新算法(更快,更小) 4.提 ...
- [LGP2758]编辑距离
目录 题目 题目描述 输入格式 输出格式 输入输出样例 题目分析 状态转移方程 初始状态 结束状态 Code 题目 题目描述 设A和B是两个字符串.我们要用最少的字符操作次数,将字符串A转换为字符串B ...
- 2021 ICPC Gran Premio de Mexico 2da Fecha部分题题解
前面的水题,在队友的配合下,很快就拿下了,剩下几道大毒瘤题,一直罚座三个小时,好让人自闭...但不得不说,这些题的质量是真的高! H. Haunted House 首先看这个题,大眼一扫,觉得是某种数 ...
- 如何反编译微信小程序👻
如何反编译微信小程序 准备工具: 夜神模拟器(或者你可以自己准备一个安卓模拟器,有root权限.) RE文件管理器(下载地址:https://soft.ucbug.com/uploads/shouji ...
- Go语言核心36讲(Go语言进阶技术十二)--学习笔记
18 | if语句.for语句和switch语句 现在,让我们暂时走下神坛,回归民间.我今天要讲的if语句.for语句和switch语句都属于 Go 语言的基本流程控制语句.它们的语法看起来很朴素,但 ...
- python3 调用 centos 常用系统命令
一.创建目录 1 import os 2 3 base_path = '/data/sw_backup' 4 addr= 'FT' 5 ip='192.168.1.1' 6 path = base_p ...
- ELK集群之grafana(8)
Grafana的安装和读取ES数据 模拟es数据产生sjgtest.py import time import datetime from elasticsearch import Elasticse ...
- 攻防世界 WEB 高手进阶区 easytornado Writeup
攻防世界 WEB 高手进阶区 easytornado Writeup 题目介绍 题目考点 Python模板 tornado 模板注入 Writeup 进入题目, 目录遍历得到 /flag.txt /w ...
- 【死磕 NIO】— Proactor模式是什么?很牛逼吗?
大家好,我是大明哥. 上篇文章我们分析了高性能 IO模型Reactor模式,了解了什么是Reactor 模式以及它的三种常见的模式,这篇文章,大明再介绍另外一种高性能IO模型: Proactor. 为 ...