Tree Array
一道简单但有趣的期望\(DP\),套路的,先枚举一个根,再计算答案。考虑到我只想知道 \(i < j\)且\(time_i > time_j\) 的个数,不妨枚举\(i, j\),计算\(i\)后出现的概率,求和即为答案。
对于\(i,j\),我们只关心他们的相对出现时间,那么对此有影响的便是\(LCA_{i,j}\)下方的点。设\(f_{x,y}\)表示\(i\)到\(lca\)间有\(x\)个点,\(j\)到\(lca\)间有\(y\)个点,使得\(i\)先出现的概率。因为对于每个点出现的概率是相同的,所以\(f_{x,y} = \frac{f_{x - 1,y} + f_{x, y - 1}}{2}\),初值为\(f_{0,i} = 1\)。总时间复杂度为\(O(n^3logn)\)。
Code
#include<cstdio>
#include<algorithm>
#define LL long long
using namespace std;
const int N = 205, P = 1e9 + 7;
int h[N], tot, n, g[N][N], f[N][15], dep[N];
struct edge{int to, nxt;}e[N << 1];
void add(int x, int y){e[++tot] = edge{y, h[x]}, h[x] = tot;}
LL fpow(LL x, LL y) {
LL res = 1;
for (; x; x >>= 1, y = y * y % P)
if (x & 1) res = res * y % P;
return res;
}
void dfs(int u, int fa) {
dep[u] = dep[fa] + 1, f[u][0] = fa;
for (int i = 1; i <= 10; i++) f[u][i] = f[f[u][i - 1]][i - 1];
for (int i = h[u]; i; i = e[i].nxt)
if (e[i].to ^ fa) dfs(e[i].to, u);
}
int lca(int x, int y) {
if (dep[x] > dep[y]) swap(x, y);
int tmp = dep[y] - dep[x];
for (int i = 0; tmp; tmp >>= 1, i++) (tmp & 1) && (y = f[y][i]);
if (x == y) return dep[x];
for (int i = 10; i >= 0; i--) (f[x][i] ^ f[y][i]) && (x = f[x][i], y = f[y][i]);
return dep[f[x][0]];
}
int main() {
scanf("%d",&n);
for (int i = 1, q, p; i < n; i++) scanf("%d%d",&q,&p), add(q, p), add(p, q);
for (int i = 1; i <= n; i++) g[0][i] = 1;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
g[i][j] = (g[i - 1][j] + g[i][j - 1]) * fpow(P - 2, 2LL) % P;
LL ans = 0;
for (int i = 1; i <= n; i++) {
dfs(i, 0);
for (int j = 1; j <= n; j++)
for (int k = j + 1, x; k <= n; k++)
x = lca(j, k), (ans += g[dep[k] - x][dep[j] - x]) %= P;
}
printf("%lld\n", ans * fpow(P - 2, (LL)n) % P);
}