Maze CodeForces - 123E
题目描述:一个迷宫是一棵树(即一张无向图,其中任意两点之间仅有一条路径)。迷宫的起点和终点都按照某种概率随机选取。人们会在迷宫中用深度优先搜索的方法搜寻终点。如果有许多条可能的路径,会等概率地选取一条。考虑如下伪代码:
DFS(x)
if x == exit vertex then
finish search
flag[x] <- TRUE
random shuffle the vertices' order in V(x) // here all permutations have equal probability to be chosen
for i <- 1 to length[V] do
if flag[V[i]] = FALSE then
count++;
DFS(y);
count++;
V(x)是和x相邻的顶点列表。最初flag数组的值均为false。第一次DFS的参数是迷宫的入口节点。当搜索终止,变量count的值就是在迷宫中走的步数。
你的任务是计算在迷宫中从入口到出口,所走的期望步数。
对于每个点会给出两个值xi,yi,点i作为入口的概率是xi/sigma(xi),作为出口的概率是yi/sigma(yi)
n ≤ 100000
题解:首先这显然是一个概率dp(废话╮(╯▽╰)╭)。
然后枚举起点可以yy出一个O(n^2)的树形dp,显然不符合数据规模。
那么考虑以u为起点,v为终点的一条路径,u->v之间的边一定会经过一次(1~3号),v以后的边不可能经过(4~9),它们之间的边(10~17)可能访问0或2次(一进一出2次),取决于分叉时先访问了路径上的边1~3还是旁边的10~17。分叉点访问每条边的顺序是等概率的,所以之间的边访问次数的期望为1。
目前为止初步看dp还是O(n^2)的。但实际可以O(n)转移。
我们考虑如果当前点x为终点,那么当起点在x的子树中是,从子树向上到达x就会停止,所以可能经过的边数就是起点所属的x的儿子的size,那么期望就是size*子树中点作为起点的总概率*x为终点的概率。
如果起点在x的子树之外,那么x的子树都没有机会遍历到,而剩下的边都有可能被经过,所以对答案的贡献是(n-size)*(sigma(xi)-sigma(x的子树))*x为终点的概率。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<set>
#include<vector>
using namespace std;
const int N=100000+5;
int n,u,v,sz[N];
int cnt,fir[N],tar[N<<1],nxt[N<<1];
double x[N],y[N],s[N],e[N],sums[N],ans;
inline void link(int a,int b){
tar[++cnt]=b,nxt[cnt]=fir[a],fir[a]=cnt;
}
void dfs(int x,int pre){
sz[x]=1;
for(int i=fir[x];i;i=nxt[i])
if(tar[i]!=pre){
dfs(tar[i],x);
sz[x]+=sz[tar[i]];
s[x]+=s[tar[i]];
ans+=s[tar[i]]*sz[tar[i]]*e[x];
}
ans+=e[x]*(n-sz[x])*(1-s[x]);
}
int main(){
scanf("%d",&n);
for(int i=1;i<n;i++){
scanf("%d %d",&u,&v);
link(u,v),link(v,u);
}
double X=0.0,Y=0.0;
for(int i=1;i<=n;i++){
scanf("%lf %lf",&x[i],&y[i]);
X+=x[i],Y+=y[i];
}
for(int i=1;i<=n;i++)
s[i]=x[i]/X,e[i]=y[i]/Y;
dfs(1,0);
printf("%.20f\n",ans);
}