HDU 6305 笛卡尔树

题目链接


题意:
给定一个数组 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=1nsize[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+(1p)0=2size[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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值