CF123E Maze 题解
题目链接:Maze
题意:
一棵 \(n\) 个点的树,起点和终点随机选取(概率给定)。
运行如下程序, \(V(x)\) 是与 \(x\) 相邻的顶点列表,最初 \(\mathrm{flag}\) 数组为 \(\verb!false!\),DFS 的 参数是起点
你要求 \(\mathrm{count}\) 的期望值。
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++;
输入格式:
第一行给定 \(n\) ,接下来 \(n-1\) 行给定边
然后 \(n\) 行给定每个点作为 \(S,T\) 的概率(给定整数 \(x_i,y_i\) ,概率为 \(\frac{x_i}{\sum_j x_j}\) 和 \(\frac{y_i}{\sum_j y_j}\))。
输出格式:
误差不超过 \(10^{-9}\) 。
数据范围:
\(1 \le n \le 10^5\) 。
注意这个题目的代码,对于一些点会有额外的“回程费”,而另一些则没有。
考虑一对 \((S,T)\) ,将 \(S\) 临时定为根,则
对于 \(T\) 子树内的点,这些点不会被遍历到,贡献为 \(0\) 。
对 \(S \leadsto T\) 的最短路径上的点,这些点只会被遍历一次(不回程),贡献为 \(1\) 。
对于除此以外的其他点,例如点 \(x\) ,当我们走到 \(fa(x)\) 时,
- 有 \(\frac{1}{2}\) 的概率走到 \(x\) ,此时 \(x\) 的贡献为 \(2\) ;
- 还有 \(\frac{1}{2}\) 的概率走到 \(T\) 的方向,此时 \(x\) 的贡献为 \(0\) 。
于是这样的 \(x\) 的贡献为 \(\frac{1}{2}\times 2 + \frac{1}{2}\times 0 = 1\) 。
这里的 \(\frac{1}{2}\) 是考察有多少种排列中 \(x\) 在 \(T\) 方向那个节点的前面。
于是问题就变成了,对于每一对 \((S,T)\) ,以 \(S\) 为根时 \(n-\mathrm{size}_S(T)\) 的期望。
考虑枚举 \(T\) ,然后统计 \(S\) ,
- \(S=T\) ,此时 \(\mathrm{size}_S(T)=n\) 。
- \(S\) 在 \(T\) 的子树内,显然 \(\mathrm{size}_S(T) = n - \mathrm{size}(S)\) 。
- \(S\) 不在 \(T\) 的子树内,则 \(\mathrm{size}_S(T)=\mathrm{size}(T)\) 。
那么我们只需要扫一遍,用每种 \(S\) 的概率 \(\times\) 它的贡献,加起来以后乘上当前节点作为 \(T\) 的概率即可。
时间复杂度 \(\mathcal{O}(n)\)
代码:
#include <bits/stdc++.h>
using namespace std;
// #define int long long
// #define INF 0x3f3f3f3f3f3f3f3f
void up(int &x,int y) { x < y ? x = y : 0; }
void down(int &x,int y) { x > y ? x = y : 0; }
#define N ((int)(1e5 + 15))
int n, _ps[N], _pt[N], sz[N];
double S, T, res, ps[N], pt[N], p[N];
vector<int> vec[N];
void dfs(int u, int fa)
{
sz[u] = 1; p[u] = ps[u];
double sum = 0;
for(int v : vec[u]) if(v != fa)
{
dfs(v, u); sz[u] += sz[v];
p[u] += p[v]; sum += p[v] * sz[v];
}
sum += (1 - p[u]) * (n - sz[u]); res += sum * pt[u];
}
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
// freopen("check.in","r",stdin);
// freopen("check.out","w",stdout);
cin >> n;
for(int i = 1, u, v; i < n; i++)
{
cin >> u >> v;
vec[u].push_back(v); vec[v].push_back(u);
}
for(int i = 1; i <= n; i++) {
cin >> _ps[i] >> _pt[i]; S += _ps[i]; T += _pt[i];
}
for(int i = 1; i <= n; i++) {
ps[i] = _ps[i] / S; pt[i] = _pt[i] / T;
}
dfs(1, 0); cout << fixed << setprecision(12) << res << '\n';
return 0;
}
参考文献: