Luckyleaves's Blog

stay hungry,stay foolish greedy and lucky

CF1540B Tree Array

树形$dp$+期望。

算是一道不错的好题。

1
给定一棵树,随机确定一个根,然后将其加入一个数列,每次从所有和已经加入数列中的点相邻的未标记的点中等概率选取一个点加入队列,求最终队列的期望逆序对数

Solution

首先我们分别求出每个点的为根节点时最后的期望逆序对数,最后加在一起除以$n$即可,此时我们再来思考以每个点为根时,每个逆序对的期望贡献,考虑逆序对$(a,b)$,当我们拓展到$lca(a,b)$时,此时对先走到$a$还是先走到$b$才会产生影响,设当前每个点有$p$的概率走到,设$f_{i,j}$表示走到$a$还有$i$步,走到$b$还有$j$步的期望贡献,显然有以下转移方程:$f_{i,j}=(1-2\cdot p)\cdot f_{i,j}+p\cdot f_{i-1,j-1}+p\cdot f_{i,j-1}$,前面表示这一步走的其他人,后面那个表示直接贡献,化简之后是一个与$p$无关的表达式$f_{i,j}=\frac{f_{i-1,j}+f_{i,j-1}}{2}$,所有的$\forall i \ge 1,f_{0,i}=1$,之后就可以开始$dp$了,那么当然每对逆序对$(a,b)$的贡献自然应该是$f_{depth[i]-depth[lca(a,b)],depth[b]-dpeth[lca(a,b)]}$。

复杂度$O(n^3)$。

常数比较小,喜提最优解。

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
#pragma GCC optimize("O2,O3,Ofast")
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<queue>
#include<ctime>
#include<map>
#include<list>
#include<random>
#define LL long long
#define PII pair<int, int>
using namespace std;
template <class T>
inline void read(T &res)
{
res = 0; bool flag = 0;
char c = getchar();
while(c < '0' || '9' < c) { if(c == '-') flag = 1; c = getchar(); }
while('0' <= c && c <= '9') res = (res << 3) + (res << 1) + (c ^ 48), c = getchar();
if(flag) res = -res;
}
template <class T, class ...ARC>
inline void read(T &res, ARC &...com) { read(res), read(com...); }
template <class T>
inline void write(T res)
{
if(res < 0) res = -res, putchar('-');
if(res > 9) write(res / 10);
putchar(res % 10 + '0');
}
template <>
inline void write(char s) { putchar(s); }
template <>
inline void write(const char *s) { while(*s) putchar(*s ++); }
template <class T, class ...ARC>
inline void write(T res, ARC ...com) { write(res), write(com...); }
const int N = 205, mod = 1e9 + 7, inv = (mod + 1) >> 1;
int n;
int idx;
int e[N << 1], ne[N << 1], h[N];
inline void add(int x, int y)
{
idx ++;
e[idx] = y, ne[idx] = h[x], h[x] = idx;
}
int depth[N], fa[N << 1][8], tot, id[N], lg[N << 1];
int f[N][N];
inline void dfs(int x, int y)
{
fa[++ tot][0] = x;
id[x] = tot;
depth[x] = depth[y] + 1;
for(int i = h[x], j; ~i;i = ne[i])
{
j = e[i];
if(j == y) continue;
dfs(j, x);
fa[++ tot][0] = x;
}
}
inline int check_max(int x, int y)
{
if(depth[x] <= depth[y]) return x;
return y;
}
inline void init()
{
for(int i = 2;i <= tot;i ++) lg[i] = lg[i >> 1] + 1;
for(int j = 1;j <= lg[tot];j ++)
for(int i = 1;i + (1 << j) - 1 <= tot;i ++)
fa[i][j] = check_max(fa[i][j - 1], fa[i + (1 << j - 1)][j - 1]);
}
inline int lca(int x, int y)
{
int l = id[x], r = id[y], len;
if(l > r) swap(l, r);
len = lg[r - l + 1];
return check_max(fa[l][len], fa[r - (1 << len) + 1][len]);
}
int ans;
inline void calc(int x)
{
tot = 0;
depth[x] = 0;
dfs(x, x);
init();
for(int i = 1;i <= n;i ++)
for(int j = 1;j < i;j ++)
ans = (ans + f[depth[i] - depth[lca(i, j)]][depth[j] - depth[lca(i, j)]]) % mod;
}
inline int qpow(int x, int k)
{
int res = 1;
while(k)
{
if(k & 1) res = 1ll * res * x % mod;
x = 1ll * x * x % mod;
k >>= 1;
}
return res;
}
int main()
{
memset(h, -1, sizeof(h));
read(n);
for(int i = 1, o, u;i < n;i ++) read(o, u), add(o, u), add(u, o);
for(int i = 1;i <= n;i ++) f[0][i] = 1;
for(int i = 1;i <= n;i ++)
for(int j = 1;j <= n;j ++)
f[i][j] = 1ll * (f[i][j - 1] + f[i - 1][j]) % mod * inv % mod;
for(int i = 1;i <= n;i ++) calc(i);
write(1ll * ans * qpow(n, mod - 2) % mod);
return 0;
}