【BZOJ2707】[SDOI2012]走迷宫 Tarjan+拓扑排序+高斯消元+期望
【BZOJ2707】[SDOI2012]走迷宫
Description
Input
Output
|
测试点
|
N
|
M
|
Hint
|
|
[1, 6]
|
<=10
|
<=100
|
|
|
[7, 12]
|
<=200
|
<=10000
|
|
|
[13, 20]
|
<=10000
|
<=1000000
|
保证强连通分量的大小不超过100
|
题解:先Tarjan缩环,然后对于强连通分量内部的点,用高斯消元,外部的DAG用拓扑排序+DP。具体细节还是说一下吧:
1.高斯消元时,列方程$f[i]=\sum f[j]/d[i]+1$,发现i的某些出边可能指向强连通分量外的点,把它们都当成常数项就好了。特别地,T的方程要特殊处理。
2.判断INF时比较坑,从S开始BFS,我们的DAG只能包含我们能DFS到的点,并且,搜到T时要立刻停止。DFS后,如果发现一个强连通分量的出度为0且不是T所在的强连通分量,则INF。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <vector>
#include <queue>
#include <cmath>
const int maxn=10010;
using namespace std;
int n,m,cnt,Cnt,tot,top,sum,S,T;
vector<int> s[maxn];
int dep[maxn],low[maxn],head[maxn],to[1000010],next[1000010],sta[maxn],d[maxn],ins[maxn],D[maxn],pos[maxn],bel[maxn];
int To[1000010],Next[1000010],Head[maxn],vis[maxn];
double p[maxn],v[110][110];
queue<int> q;
inline void add(int a,int b)
{
to[cnt]=b,next[cnt]=head[a],head[a]=cnt++;
}
inline void Add(int a,int b)
{
To[Cnt]=b,Next[Cnt]=Head[a],Head[a]=Cnt++;
}
void tarjan(int x)
{
dep[x]=low[x]=++tot,sta[++top]=x,ins[x]=1;
int i,t;
for(i=Head[x];i!=-1;i=Next[i])
{
if(!dep[To[i]]) tarjan(To[i]),low[x]=min(low[x],low[To[i]]);
else if(ins[To[i]]) low[x]=min(low[x],dep[To[i]]);
}
if(dep[x]==low[x])
{
sum++;
do
{
t=sta[top--],ins[t]=0,bel[t]=sum,pos[t]=s[sum].size(),s[sum].push_back(t);
}while(t!=x);
}
}
void calc(int x)
{
int i,j,k,a,b,nm=s[x].size();
for(i=0;i<nm;i++) memset(v[i],0,sizeof(v[0][0])*(nm+1));
for(i=0;i<nm;i++)
{
a=s[x][i];
for(j=head[a];j!=-1;j=next[j])
{
b=to[j];
if(bel[b]==bel[a]) v[pos[b]][pos[a]]+=1.0/d[b],v[pos[b]][nm]-=1.0/d[b];
}
}
for(i=0;i<nm;i++)
{
v[i][nm]-=p[s[x][i]];
if(s[x][i]==T) for(j=0;j<=nm;j++) v[i][j]=0;
v[i][i]-=1;
}
for(i=0;i<nm;i++)
{
for(j=i+1;j<nm;j++) if(fabs(v[j][i])>fabs(v[i][i])) for(k=i;k<=nm;k++) swap(v[j][k],v[i][k]);
double tmp=v[i][i];
if(fabs(tmp)<1e-9) continue;
for(k=i;k<=nm;k++) v[i][k]/=tmp;
for(j=0;j<nm;j++) if(i!=j)
{
tmp=v[j][i];
for(k=i;k<=nm;k++) v[j][k]-=v[i][k]*tmp;
}
}
for(i=0;i<nm;i++) p[s[x][i]]=v[i][nm];
}
void dfs(int x)
{
vis[x]=1;
if(x==T) return ;
for(int i=Head[x];i!=-1;i=Next[i])
{
if(bel[To[i]]!=bel[x]) D[bel[x]]++;
if(!vis[To[i]]) dfs(To[i]);
}
}
inline int rd()
{
int ret=0,f=1; char gc=getchar();
while(gc<'0'||gc>'9') {if(gc=='-')f=-f; gc=getchar();}
while(gc>='0'&&gc<='9') ret=ret*10+gc-'0',gc=getchar();
return ret*f;
}
int main()
{
n=rd(),m=rd(),S=rd(),T=rd();
int i,j,a,b,u,v;
memset(head,-1,sizeof(head)),memset(Head,-1,sizeof(Head));
for(i=1;i<=m;i++) a=rd(),b=rd(),Add(a,b),add(b,a),d[a]++;
for(i=1;i<=n;i++) if(!dep[i]) tarjan(i);
dfs(S);
for(i=1;i<=n;i++) if(vis[i]&&bel[i]!=bel[T]&&!D[bel[i]])
{
printf("INF");
return 0;
}
q.push(bel[T]);
while(!q.empty())
{
u=q.front(),q.pop();
calc(u);
if(u==bel[S])
{
printf("%.3lf",fabs(p[S]));
return 0;
}
for(i=0;i<(int)s[u].size();i++) for(v=s[u][i],j=head[v];j!=-1;j=next[j]) if(bel[to[j]]!=bel[v])
{
D[bel[to[j]]]--,p[to[j]]+=(p[v]+1)/d[to[j]];
if(!D[bel[to[j]]]) q.push(bel[to[j]]);
}
}
printf("INF");
return 0;
}//9 12 1 6 1 2 2 3 3 1 3 4 3 7 4 5 5 6 6 4 6 7 7 8 8 9 9 7
【BZOJ2707】[SDOI2012]走迷宫 Tarjan+拓扑排序+高斯消元+期望的更多相关文章
- HDU2262;Where is the canteen(高斯消元+期望)
传送门 题意 给出一张图,LL从一个点等概率走到上下左右位置,询问LL从宿舍走到餐厅的步数期望 分析 该题是一道高斯消元+期望的题目 难点在于构造矩阵,我们发现以下结论 设某点走到餐厅的期望为Ek 1 ...
- BZOJ2707 [SDOI2012]走迷宫 【概率dp + tarjan + 高斯消元】
题目 Morenan被困在了一个迷宫里.迷宫可以视为N个点M条边的有向图,其中Morenan处于起点S,迷宫的终点设为T.可惜的是,Morenan非常的脑小,他只会从一个点出发随机沿着一条从该点出发的 ...
- BZOJ2707: [SDOI2012]走迷宫(期望 tarjan 高斯消元)
题意 题目链接 Sol 设\(f[i]\)表示从\(i\)走到\(T\)的期望步数 显然有\(f[x] = \sum_{y} \frac{f[y]}{deg[x]} + 1\) 证明可以用全期望公式. ...
- BZOJ 2707: [SDOI2012]走迷宫 拓扑+高斯消元+期望概率dp+Tarjan
先Tarjan缩点 强连通分量里用高斯消元外面直接转移 注意删掉终点出边和拓扑 #include<cstdio> #include<cstring> #include<a ...
- BZOJ 2707: [SDOI2012]走迷宫( tarjan + 高斯消元 )
数据范围太大不能直接高斯消元, tarjan缩点然后按拓扑逆序对每个强连通分量高斯消元就可以了. E(u) = 1 + Σ E(v) / degree(u) 对拍时发现网上2个程序的INF判断和我不一 ...
- BZOJ2707 : [SDOI2012]走迷宫
首先求出SCC缩点,E[T]=0,按拓扑序计算 对于无边连出的块,如果不是T所在块,则称该块是死路块 对于一个块,如果其中的点连出的边是死路块,则它也是死路块 否则对于每块进行高斯消元求出期望 如果S ...
- BZOJ 3143 HNOI2013 游走 高斯消元 期望
这道题是我第一次使用高斯消元解决期望类的问题,首发A了,感觉爽爽的.... 不过笔者在做完后发现了一些问题,在原文的后面进行了说明. 中文题目,就不翻大意了,直接给原题: 一个无向连通图,顶点从1编号 ...
- 【LOJ2542】【PKUWC 2018】随机游走 min-max容斥 树上高斯消元
题目描述 有一棵 \(n\) 个点的树.你从点 \(x\) 出发,每次等概率随机选择一条与所在点相邻的边走过去. 有 \(q\) 次询问,每次询问给定一个集合 \(S\),求如果从 \(x\) 出发一 ...
- [luogu3232 HNOI2013] 游走 (高斯消元 期望)
传送门 题目描述 一个无向连通图,顶点从1编号到N,边从1编号到M. 小Z在该图上进行随机游走,初始时小Z在1号顶点,每一步小Z以相等的概率随机选 择当前顶点的某条边,沿着这条边走到下一个顶点,获得等 ...
随机推荐
- python抓包截取http记录日志
#!/usr/bin/python import pcap import dpkt import re def main(): pc=pcap.pcap(name="eth1" ...
- 【SpringMVC学习02】走进SpringMVC的世界
上一篇博文主要介绍了springmvc的整个架构执行流程,对springmvc有了宏观上的把握,这一篇博文主要以案例作为驱动,带你走进springmvc的世界.案例中的需求很简单:对商品列表的查询.表 ...
- 使用SAS令牌连接Azure EventHub
概述 事件中心使用在命名空间和事件中心级别提供的共享访问签名.SAS令牌是从SAS密钥生成的,它是以特定格式编码的URL的SHA哈希. 事件中心可以使用密钥(策略)的名称和令牌重新生成哈希,以便对发送 ...
- Python数据处理——绘制函数图形以及数据拟合
1.多项式拟合 对散点进行多项式拟合并打印出拟合函数以及拟合后的图形import matplotlib.pyplot as pltimport numpy as npx=np.arange(1,17, ...
- C语言学习笔记(二) 基础知识
数据类型 C语言数据可以分为两大类: 基本类型数据和复合类型数据: 基本类型数据 整数 整型 (int) ——占4字节 短整型(short int) ——占2字节 长整型(long in ...
- Redis之ZSet命令
0.前言 Redis有序集合ZSet可以按分数进行排序, 存储结构可能使用ziplist,skiplist和hash表, zset_max_ziplist_entries和zset_max_zipli ...
- 红茶一杯话Binder (传输机制篇_中)
红茶一杯话Binder (传输机制篇_中) 侯 亮 1 谈谈底层IPC机制吧 在上一篇文章的最后,我们说到BpBinder将数据发到了Binder驱动.然而在驱动层,这部分数据又是如何传递到BBind ...
- shell脚本之微信报警功能的实现
导语:现在越来越流行微信报警功能了.下面就来看看具体实现吧! 1.先申请一个微信企业号 传送门:http://work.weixin.qq.com/ 2.添加用户 2.创建应用 3.创建管理组并添加管 ...
- Harmonic Number 求Hn; Hn = 1 + 1/2 + 1/3 + ... + 1/n; (n<=1e8) T<=1e4; 精确到1e-8; 打表或者调和级数
/** 题目:Harmonic Number 链接:https://vjudge.net/contest/154246#problem/I 题意:求Hn: Hn = 1 + 1/2 + 1/3 + . ...
- Linux Linux程序练习二
/* 编写一个程序读取a.txt文件,将文件内容数字从小到大排序,并将排序结果写入b.txt. */ #include <stdio.h> #include <stdlib.h> ...