题意:
给定一个数组
a
a
a,现在存在一个数组
b
b
b,其元素值在
[
0
,
1
]
[0,1]
[0,1]随机生成,若数组
a
,
b
a,b
a,b生成的笛卡尔树同构,则数组
b
b
b的价值为
∑
b
[
i
]
\sum b[i]
∑b[i],否则为
0
0
0,求数组
b
b
b的期望价值为多少?
思路:
首先构建
a
a
a数组的笛卡尔树,此时有一个结论是:
b
b
b数组与
a
a
a数组同构的概率是
p
=
∏
i
=
1
n
1
s
i
z
e
[
i
]
p = \prod_{i=1}^{n} \frac{1}{size[i]}
p=i=1∏nsize[i]1
s
i
z
e
[
i
]
size[i]
size[i]代表以第
i
i
i个节点为根的子树大小。
为什么成立呢?假设我们先生成 n n n个 [ 0 , 1 ] [0,1] [0,1]的互不相等的数,随后全排列,假设 a a a数组中最大的数的下标为 i d id id,则 b b b数组中下标 i d id id处也应该填入最大的数,而此条件成立的概率为 1 n \frac{1}{n} n1。此时对于 i d id id左右的两个区间,同样需要满足该条件,即最大值下标应该相同。将所有独立区间满足条件的概率乘起来,即为总概率。
随后,因为每个数都在[0,1]中等概率随机生成,则期望值为
1
/
2
1/2
1/2,故
n
n
n个数和的期望值为
n
/
2
n/2
n/2。
此时期望价值为:
n
2
p
+
(
1
−
p
)
∗
0
=
n
2
∏
s
i
z
e
[
i
]
\frac{n}{2}p + (1-p)*0 = \frac{n}{2\prod size[i]}
2np+(1−p)∗0=2∏size[i]n
故 d f s dfs dfs统计一下笛卡尔树每个节点为根的子树的 s i z e size size即可。
代码:
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<set>
using namespace std;
typedef long long ll;
typedef long double ld;
typedef pair<int, int> PII;
const int mod = 1e9 + 7;
const int A = 1e6 + 10;
int n, tot, Sta[A], L[A], R[A], vis[A];
ll Inv[A], ans;
PII a[A];
int dfs(int u){
int siz = 1;
if (L[u]) siz += dfs(L[u]);
if (R[u]) siz += dfs(R[u]);
ans = ans * Inv[siz] % mod;
return siz;
}
int build_Tree(){
tot = 0;
for (int i = 1; i <= n; i++) L[i] = R[i] = vis[i] = 0;
for (int i = 1; i <= n; i++) {
int k = tot;
while (k > 0 && a[Sta[k - 1]] < a[i]) k--;
if (k) R[Sta[k - 1]] = i;
if (k < tot) L[i] = Sta[k];
Sta[k++] = i;
tot = k;
}
for (int i = 1; i <= n; i++) vis[L[i]] = vis[R[i]] = 1;
int rt = 0;
for (int i = 1; i <= n; i++) {
if (vis[i] == 0) rt = i;
}
return rt;
}
int main(){
Inv[1] = 1;
for (int i = 2; i < A; i++) Inv[i] = Inv[mod%i] * (mod - mod / i) % mod;
int T;
scanf("%d", &T);
while (T--) {
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
int x;
scanf("%d", &x);
a[i] = make_pair(x, -i);
}
ans = 1LL * n * Inv[2] % mod;
int rt = build_Tree();
dfs(rt);
printf("%I64d\n", ans);
}
return 0;
}