题目翻译:(翻译来自: )
一个迷宫是一棵树(即一张无向图,其中任意两点之间仅有一条路径)。迷宫的起点和终点都按照某种概率随机选取。人们会在迷宫中用深度优先搜索的方法搜寻终点。如果有许多条可能的路径,会等概率地选取一条。考虑如下伪代码:
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的值就是在迷宫中走的步数。
你的任务是计算在迷宫中从入口到出口,所走的期望步数。
题解:
首先一看知道题就知道应该去求每个点对答案的贡献
现在考虑如何求出这个贡献。
我们发现如果我们单独枚举一个点,那么这个点的情况一共有三种:
1.必定被经过
2.必定不被经过
3.不一定被经过
这样就不好统计了,所以我们先转换一下思维
怎样才能准确的找出经过的路径? 最直接的方法就是暴力枚举所有的起点和终点
这样路径就唯一确定了,直接在这个图中求一边期望,然后加权求和
可是 n <= 10^5,暴力肯定无法通过,但是有值得我们借鉴的思维
枚举端点
我们可以枚举终点,这样情况就分成了。。。还是三种:
1.起点就是终点,贡献为0,该情况可以忽略
2.起点在以终点为根的子树中
3.起点不在以终点为根的子树中
对于第二种情况,我们可以直接计算贡献,即(子树的节点数目*子树中有一个点作为入口的概率)
对于第三种情况,我们也可以直接计算的,即(除去子树后树的节点数目*除去子树后树中存在入口的概率)
为什么能这么计算呢?
我们考虑一下,如果根的某一个儿子是入口,那么贡献应为0.5*siz[v]*2
我们发现,如果一条路径经过n个点两次后到达出口,那么对答案的贡献即为(n<<1)
我们可以这么想,把题目中给的伪代码改动一下,那么每次到达一个点++count,退出一个点++count
最后再--count,这样就是这条路径对答案的贡献。
所以,我们直接刨去到达终点时对答案做出的贡献,那么就可以得到刚才的式子了
我们考虑进行推广,发现所有的点都满足这个式子
所以我们可以直接对若干个点直接加和,那么就是上面提到的计算方式了
O(n)
Code
1 #include2 #include 3 #include 4 using namespace std; 5 typedef long long ll; 6 inline void read(int &x){ 7 x=0;char ch;bool flag = false; 8 while(ch=getchar(),ch<'!');if(ch == '-') ch=getchar(),flag = true; 9 while(x=10*x+ch-'0',ch=getchar(),ch>'!');if(flag) x=-x;10 }11 inline int cat_max(const int &a,const int &b){ return a>b ? a:b;}12 inline int cat_min(const int &a,const int &b){ return a