HDU 6305 RMQ Similar Sequence(笛卡尔树)

题目链接:RMQ Similar Sequence

题意

首先给定一个长度为 n n 的整数序列 A={a1,a2,,an},定义 RMQ{A,l,r} R M Q { A , l , r } 表示查找区间 [l,r] [ l , r ] 内的最大值的下标,如果存在多个最大值,则取最左边的数字的下标。现在要构造一个长度为 n n 的序列 B={b1,b2,,bn},要求每个数字都是属于区间 [0,1] [ 0 , 1 ] 内的实数,且对于每一个 RMQ{B,l,r} R M Q { B , l , r } ,其询问结果都和 RMQ{A,l,r} R M Q { A , l , r } 相同,问所有满足条件的序列 B B i=1nbi 的期望值。

输入

第一行为一个整数 T T ,接下去有 T 组数据,每组数据第一行为一个整数 n (1n106) n   ( 1 ≤ n ≤ 10 6 ) ,第二行为 n n 个整数 a1,a2,,an (1ain),数据保证所有 n n 的和不超过 3×106

输出

输出期望对 109+7 10 9 + 7 取模的结果,如果期望无法被 109+7 10 9 + 7 整除,先化简期望为 pq p q ,保证 p p q 互质且都为正整数的情况下,输出 a (0a109+6) a   ( 0 ≤ a ≤ 10 9 + 6 ) 的值,要求 paq p − a q 能够被 109+7 10 9 + 7 整除。

样例

输入
3
3
1 2 3
3
1 2 1
5
1 2 3 2 1
输出
250000002
500000004
125000001
题解

根据题意建立一棵序列 A A 的笛卡尔树,题目即求笛卡尔树与数列 A 同构的序列 B B 的序列和的期望,由于 B 内每个数字都是实数,所以任意两个数字相等的概率趋近于 0 0 ,对于每一个节点对应的区间,节点的数字大于该区间内所有数字的概率为 1soni,其中 soni s o n i 为节点 i i 对应子树的节点数,将所有节点的满足条件的概率相乘,再乘上序列求和的期望值 n2,就是答案。最后要求期望对 109+7 10 9 + 7 的取模的结果,就是将结果 pq p q 转化成 p×q1mod109+7 p × q − 1 mod 10 9 + 7

过题代码

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <climits>
#include <cfloat>
#include <cstring>
#include <string>
#include <vector>
#include <list>
#include <queue>
#include <stack>
#include <map>
#include <set>
#include <algorithm>
using namespace std;

#define LL long long
const int maxn = 1000000 + 100;
const LL MOD = 1000000007;

LL ans;
LL inv[maxn];

struct Tree {
    int root, top, n;
    int sta[maxn], l[maxn], r[maxn];
    bool vis[maxn];

    void build(int *num, int nn) {
        n = nn;
        top = 0;
        memset(l, 0, sizeof(int) * (n + 1));
        memset(r, 0, sizeof(int) * (n + 1));
        memset(vis, 0, sizeof(bool) * (n + 1));
        for(int i = 1; i <= n; ++i) {
            int tmp = top;
            while(top > 0 && num[sta[top - 1]] < num[i]) {
                --top;
            }
            if(top != 0) {
                r[sta[top - 1]] = i;
            }
            if(top < tmp) {
                l[i] = sta[top];
            }
            sta[top++] = i;
        }
        for(int i = 1; i <= n; ++i) {
            vis[l[i]] = vis[r[i]] = true;
        }
        for(int i = 1; i <= n; ++i) {
            if(!vis[i]) {
                root = i;
            }
        }
    }

    int dfs(int x) {
        int cnt = 1;
        if(l[x] != 0) {
            cnt += dfs(l[x]);
        }
        if(r[x] != 0) {
            cnt += dfs(r[x]);
        }
        ans = (ans * inv[cnt]) % MOD;
        return cnt;
    }
};

int T, n;
int num[maxn];
Tree t;

void Init() {
    inv[1] = 1;
    for(int i = 2; i < maxn; ++i) {
        inv[i] = (MOD - MOD / i) * inv[MOD % i] % MOD;
    }
}

int main() {
    #ifdef Dmaxiya
    freopen("test.txt", "r", stdin);
    #endif // Dmaxiya
    ios::sync_with_stdio(false);

    Init();
    scanf("%d", &T);
    while(T--) {
        scanf("%d", &n);
        ans = (n * inv[2]) % MOD;
        for(int i = 1; i <= n; ++i) {
            scanf("%d", &num[i]);
        }
        t.build(num, n);
        t.dfs(t.root);
        printf("%I64d\n", ans);
    }

    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值