1. 程式人生 > 其它 >[UOJ96] 【集訓隊互測2015】胡策的小樹

[UOJ96] 【集訓隊互測2015】胡策的小樹

先考慮不摻金坷垃的做法。

設猴子處於 \(i\) 節點的概率為 \(f_i\),列出方程如下(\(i\) 的祖先包括自身):

\[f_i = \sum_{j為i祖先}\frac{1-p_j}{siz_j}f_j + \sum_{(i,j)}p_jf_j \\ \sum f_i = 1 \]

可以 \(O(n^3)\) 暴力高消。但是並沒有用到樹上高消的性質。

考慮到列舉祖先不太優,可以將 \(f_i\) 做字首和:

\[F_i = \sum_{j為i祖先}\frac{1-p_j}{siz_j}f_j \\ f_i = \frac{siz_i}{1-p_i}(F_i-F_{fa}) \]

代入上式:

\[\frac{siz_i}{1-p_i}(F_i-F_{fa}) = \sum_{(i,j)}p_j\frac{siz_j}{1-p_j}(F_j-F_i) + F_i \\ \left( \frac{siz_i}{1-p_i} + \sum_{(i,j)}p_j\frac{siz_j}{1-p_j} - 1 \right)F_i = \left( \frac{siz_i}{1-p_i} \right)F_{fa} + \sum_{(i,j)}p_j\frac{siz_j}{1-p_j}F_j \] \[\begin{aligned} A_i &= \frac{siz_i}{1-p_i} + \sum_{(i,j)}p_j\frac{siz_j}{1-p_j} - 1 \\ B_i &= \frac{siz_i}{1-p_i} \\ C_i &= \sum_{(i,j)}p_j\frac{siz_j}{1-p_j}F_j \\ A_iF_i &= B_iF_{fa} + C_i \end{aligned} \]

\(q_i = \frac{siz_i}{1-p_i}\)

從葉子往根求出對於每個 \(i\)\(F_i = k_iF_{fa}+b_i\),即 \(F_j=k_jF_i+b_j\)

\[\begin{aligned} C_i &= \sum_{(i,j)}p_j\frac{siz_j}{1-p_j}F_j \\ &= \sum_{(i,j)}p_jq_j(k_jF_i+b_j) \\ &= F_i\sum_{(i,j)}p_jq_jk_j + \sum_{(i,j)}p_jq_jb_j \\ \end{aligned} \] \[Sk_i = \sum_{(i,j)}p_jq_jk_j \\ Sb_i = \sum_{(i,j)}p_jq_jb_j \] \[\therefore (A_i-Sk_i)F_i = B_iF_{fa} + Sb_i \\ F_i = \cfrac{B_i}{A_i-Sk_i}F_{fa} + \cfrac{Sb_i}{A_i-Sk_i} \\ \begin{cases} k_i = \cfrac{B_i}{A_i-Sk_i} \\ b_i = \cfrac{Sb_i}{A_i-Sk_i} \\ \end{cases} \]

求出來每個節點的 \(k_i\)

後,從根開始 \(dfs\) 一遍,算出 \(F_i\) 相對於 \(F_{rt}\) 的係數,進而差分求出 \(f_i\) 相對於 \(f_{rt}\) 的係數。

代入 \(\sum f_i = 0\) 的方程,求解出 \(f_{rt}\),進而求解出所有的 \(f\) 值。答案就是 \(\sum p_if_i\)

同時發現對於葉子結點,\(b_i=0\),所以對於所有節點,\(b_i\) 均為 \(0\),計算時可以不考慮 \(b_i\)

摻了金坷垃以後(一袋能頂兩代撒),考慮到必有一個新節點的 \(p_i=0\),也就是說,一旦跳入了其子樹就不會再跳出來。而時間無窮大,可以不考慮子樹外的貢獻,只需計運算元樹內的期望即可。

總的時間複雜度就是 \(O(\sum siz_i)\),由於樹的形態隨機,期望時間複雜度為 \(O(n \log n)\)

/**
 * @file:           UOJ96.cpp
 * @author:         yaoxi-std
 * @url:            https://uoj.ac/problem/96
*/
// #pragma GCC optimize ("O2")
// #pragma GCC optimize ("Ofast", "inline", "-ffast-math")
// #pragma GCC target ("avx,sse2,sse3,sse4,mmx")
#include <bits/stdc++.h>
using namespace std;
#define debug(fmt, ...) \
    fprintf(stderr, "[%s:%d] " fmt "\n", __FILE__, __LINE__, ##__VA_ARGS__)
using ll = long long;
const int MAXN = 5e5 + 10;
const int INF = 0x3f3f3f3f;
int n, dfc, a[MAXN], fa[MAXN], dfn[MAXN], siz[MAXN];
double p[MAXN], q[MAXN], k[MAXN], f[MAXN];
vector<int> son[MAXN];
void dfs(int u) {
    siz[dfn[u] = ++dfc] = 1;
    for (auto v : son[u]) dfs(v), fa[dfn[v]] = dfn[u], siz[dfn[u]] += siz[dfn[v]];
}
double work(int u) {
    const int L = u, R = u + siz[u] - 1;
    for (int i = L; i <= R; ++i) {
        p[i] = (i == 1 ? 0 : (a[i] + n - a[u]) % n * 1.0 / n);
        q[i] = siz[i] / (1 - p[i]), k[i] = q[i] - 1;
    }
    for (int i = R; i >= L + 1; --i) k[i] = q[i] / k[i], k[fa[i]] -= p[i] * q[i] * (k[i] - 1);
    double sumk = 0, ret = 0; k[u] = f[u] = 1;
    for (int i = L + 1; i <= R; ++i) k[i] *= k[fa[i]], f[i] = k[i] - k[fa[i]];
    for (int i = L; i <= R; ++i) sumk += (f[i] *= q[i]);
    for (int i = L; i <= R; ++i) ret += f[i] * (1 / sumk) * p[i];
    return ret;
}
signed main() {
    scanf("%d%d", &n, &fa[1]);
    for (int i = 2; i <= n; ++i) scanf("%d", &fa[i]), son[fa[i]].push_back(i);
    double ans = 0; dfs(1);
    for (int i = 1; i <= n; ++i) scanf("%d", &a[dfn[i]]);
    for (int i = 1; i <= n; ++i) ans = max(ans, work(i));
    return printf("%.9lf\n", ans), 0;
}