定义$pa[i]、pb[i]$为i
结点被Amadea、Bilva染色的概率。
由于Amadea 和 Bilva对树染色是两个相互独立的事件, 所以i
结点被染色的概率$p[i] = pa[i] + pb[i] - pa[i] * pb[i]$。
被染色结点数的期望即为$\sum\limits_1^np[i]$
现在考虑如何求出$pa[i]和pb[i]$, 由于两个问题是相似的, 在此只以pa举例。
观察规则
Amadea 随机均匀的挑选树中的一个节点,并将其着色。然后从该节点开始沿着树向上走,直到根节点为止,每到达的第 A
个节点也将被她着色。
设$cnta[i]$为, 以每一个结点为起点染色, i
结点总共被染色的次数。则显然$pa[i] = cnta[i] / n$.
所以只需考虑如何求出$cnta[i]$。
首先明确的是, 叶子结点的cnta是确定的即为1, 所以可以考虑自底向上DP。
i
顶点每次被染色, 其第a、2a、3a…个祖先都被会染色。
我们在dfs时记录下从根结点到当前结点的路径,这样我们就得到了当前结点的所有祖先。
此时可以注意到, 由类似前缀和的思想, 我们只需要让i
的第a的祖先加上cnta[i]即可, 这个增量在回溯到i
的第a个祖先时会继续向上累加。
由此解决。
时间复杂度
$O(n)$
#include <bits/stdc++.h>
using namespace std;
const int N = 5e5 + 10;
vector<int> g[N];
int cnta[N], cntb[N];
void dfs(int u, int v, int cnt[], vector<int> &path) {
path.push_back(u);
for (auto &s : g[u]) dfs(s, v, cnt, path);
cnt[u] ++, path.pop_back();
if (path.size() >= v) cnt[path[path.size() - v]] += cnt[u];
}
int main () {
int T; cin >> T;
for (int t = 1; t <= T; t ++) {
int n, a, b; cin >> n >> a >> b;
for (int i = 1; i <= n; i ++) {
g[i].resize(0), cnta[i] = 0, cntb[i] = 0;
}
for (int i = 2, fa; i <= n; i ++) {
cin >> fa, g[fa].push_back(i);
}
vector<int> path;
dfs(1, a, cnta, path), dfs(1, b, cntb, path);
double res = 0;
for (int i = 1; i <= n; i ++) {
double pa = 1.0 * cnta[i] / n, pb = 1.0 * cntb[i] / n;
res += pa + pb - pa * pb;
}
printf("Case #%d: %.6lf\n", t, res);
}
return 0;
}