1 条题解

  • 0
    @ 2025-11-6 20:22:26

    题解:

    • 有 ( N ) 段墙,每段墙的高度可以是 ( a_i ) 或 ( b_i )。
    • 对于一种高度选择方案,水位计算方式为:
      • 位置 ( i ) 的水位 = 从它向左延伸到某个 ( l ),向右延伸到某个 ( r ),使得 ( h_l \ge h ) 且 ( h_r \ge h ) 的最大 ( h )。
      • 特别地,位置 1 和 N 的水位等于墙高。
      • 积水 = 水位 - 墙高。
    • 要求:对所有 ( 2^N ) 种可能的高度选择,计算它们的 总积水量之和,并取模 ( 10^9+7 )。

    关键思路

    1. 水位与墙高的关系

    对于一段墙 ( i ),它的水位实际上等于: [ \text{waterlevel}[i] = \min\left( \max_{j \le i} h_j, \max_{k \ge i} h_k \right) ] 因为水位是由它左边最高和右边最高两者中的较小值决定的。

    所以: [ \text{积水}[i] = \min\left( \max_{j \le i} h_j, \max_{k \ge i} h_k \right) - h_i ]


    2. 问题转化

    我们要计算: [ \sum_{\text{所有方案}} \sum_{i=1}^N \left[ \min\left( \max_{j \le i} h_j, \max_{k \ge i} h_k \right) - h_i \right] ]

    可以拆成: [ \sum_{\text{所有方案}} \sum_{i=1}^N \min\left( \max_{j \le i} h_j, \max_{k \ge i} h_k \right) - \sum_{\text{所有方案}} \sum_{i=1}^N h_i ]


    3. 分别计算

    3.1 第二部分:所有方案中所有墙高之和

    对于位置 ( i ),它在所有方案中出现的次数是 ( 2^{N-1} ) 次(因为其它位置任意),并且每次出现的高度要么是 ( a_i ) 要么是 ( b_i ),概率均等。

    所以: [ \sum_{\text{所有方案}} \sum_{i=1}^N h_i = 2^{N-1} \sum_{i=1}^N (a_i + b_i) ]


    3.2 第一部分:所有方案中所有位置的水位之和

    对于位置 ( i ),它的水位是: [ \min\left( \max_{j \le i} h_j, \max_{k \ge i} h_k \right) ]

    我们要对所有方案求和。


    4. 计算水位贡献

    考虑固定位置 ( i ),我们想计算在所有方案中,它的水位值之和。

    设:

    • ( L = \max_{j \le i} h_j )
    • ( R = \max_{k \ge i} h_k )
    • 水位 = ( \min(L, R) )

    我们可以枚举 ( x )(水位值),计算水位 (\ge x) 的方案数,然后累加得到水位之和。


    4.1 水位 ≥ x 的条件

    水位 ≥ x 当且仅当:

    • 存在 ( l \le i ) 且 ( h_l \ge x )
    • 存在 ( r \ge i ) 且 ( h_r \ge x )

    因为这样才能保证 ( L \ge x ) 且 ( R \ge x ),从而 ( \min(L,R) \ge x )。


    4.2 容斥计算方案数

    设:

    • ( p_j ) = 位置 ( j ) 的高度 ≥ x 的概率(在随机选择 ( a_j ) 或 ( b_j ) 时)。
      • 如果 ( a_j \ge x ) 且 ( b_j \ge x ),则 ( p_j = 1 )
      • 如果 ( a_j \ge x ) 且 ( b_j < x ),则 ( p_j = 1/2 )
      • 如果 ( a_j < x ) 且 ( b_j \ge x ),则 ( p_j = 1/2 )
      • 如果 ( a_j < x ) 且 ( b_j < x ),则 ( p_j = 0 )

    那么:

    • 事件 A = 左边存在高度 ≥ x:概率 = ( 1 - \prod_{j=1}^i (1 - p_j) )
    • 事件 B = 右边存在高度 ≥ x:概率 = ( 1 - \prod_{j=i}^N (1 - p_j) )

    水位 ≥ x 的概率 = ( P(A \cap B) )。


    4.3 利用线性性求和

    我们想要: [ \sum_{\text{方案}} \text{水位}(i) = \sum_{x \ge 1} #{\text{方案} \mid \text{水位}(i) \ge x} ] 因为水位是整数。

    所以: [ \text{水位和} = \sum_{x=1}^{\max a_i,b_i} \text{方案数}(水位(i) \ge x) ] 方案数 = ( 2^N \times P(\text{水位}(i) \ge x) )。


    5. 高效计算

    直接枚举 ( x ) 从 1 到 ( 10^9 ) 不可行。但注意到 ( p_j ) 只会在 ( x ) 跨越某个 ( a_j ) 或 ( b_j ) 时变化,所以 ( x ) 的取值只有 ( O(N) ) 个关键点。

    我们可以离散化所有 ( a_i, b_i ),然后按 ( x ) 从大到小扫描,用线段树或 Fenwick 树维护前缀和后缀的 ( 1-p_j ) 乘积,以及乘积之和的更新。


    6. 最终算法

    1. 离散化所有 ( a_i, b_i ) 得到关键值 ( v_1 < v_2 < \dots < v_m )。
    2. 对每个位置 ( i ),预处理它在每个 ( v_t ) 处的 ( p_{i,t} )。
    3. 按 ( x ) 从大到小处理:
      • 更新每个位置 ( i ) 的 ( 1-p_i ) 值。
      • 维护前缀乘积 ( \text{pref}[i] = \prod_{j=1}^i (1-p_j) ) 和后缀乘积 ( \text{suff}[i] = \prod_{j=i}^N (1-p_j) )。
      • 对每个 ( i ),计算 ( P(\text{水位} \ge x) = 1 - \text{pref}[i] - \text{suff}[i] + \text{pref}[i] \cdot \text{suff}[i] )(容斥)。
      • 累加贡献。
    4. 得到第一部分总和,减去第二部分总和,取模。

    7. 时间复杂度

    • 离散化 ( O(N \log N) )
    • 扫描 ( O(N \log N) )(使用 Fenwick 树维护乘积)

    C 语言实现框架

    由于代码较长,这里给出核心框架,省略一些细节:

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <stdbool.h>
    
    #define MOD 1000000007
    #define MAXN 500005
    
    typedef long long ll;
    
    int N;
    int a[MAXN], b[MAXN];
    int vals[2*MAXN], vcnt;
    int posA[MAXN], posB[MAXN]; // 离散化后的位置
    
    // 快速幂
    ll powmod(ll a, ll e) {
        ll res = 1;
        while (e) {
            if (e & 1) res = res * a % MOD;
            a = a * a % MOD;
            e >>= 1;
        }
        return res;
    }
    
    // Fenwick 树维护前缀后缀乘积
    ll pref[2*MAXN], suff[2*MAXN];
    ll inv2;
    
    void update(ll *bit, int i, ll val) {
        for (; i <= N; i += i & -i)
            bit[i] = bit[i] * val % MOD;
    }
    
    ll query(ll *bit, int i) {
        ll res = 1;
        for (; i; i -= i & -i)
            res = res * bit[i] % MOD;
        return res;
    }
    
    int main() {
        inv2 = powmod(2, MOD-2);
    
        scanf("%d", &N);
        for (int i = 1; i <= N; i++) {
            scanf("%d", &a[i]);
            vals[vcnt++] = a[i];
        }
        for (int i = 1; i <= N; i++) {
            scanf("%d", &b[i]);
            vals[vcnt++] = b[i];
        }
    
        // 离散化
        // 排序去重 vals...
    
        // 预处理每个位置在每个值处的 p
        // 这里简化,直接扫描...
    
        // 初始化 Fenwick
        for (int i = 1; i <= N; i++) {
            pref[i] = 1;
            suff[i] = 1;
        }
    
        ll total = 0;
        // 按 x 从大到小扫描
        for (int k = vcnt-1; k >= 0; k--) {
            int x = vals[k];
            // 更新所有位置 p 值变化
            // 更新 Fenwick
            // 计算每个 i 的 P(水位 >= x)
            // 累加 total += 方案数
        }
    
        // 第二部分
        ll sumH = 0;
        for (int i = 1; i <= N; i++)
            sumH = (sumH + a[i] + b[i]) % MOD;
        ll part2 = powmod(2, N-1) * sumH % MOD;
    
        ll ans = (total - part2 + MOD) % MOD;
        printf("%lld\n", ans);
    
        return 0;
    }
    

    总结

    这道题的关键是:

    1. 理解水位 = min(左边最大, 右边最大)
    2. 用概率和容斥计算方案数
    3. 离散化 + 扫描线 + 数据结构维护乘积

    由于 N 可达 5e5,必须用 O(N log N) 的算法。

    • 1

    信息

    ID
    4911
    时间
    1000ms
    内存
    256MiB
    难度
    7.5
    标签
    递交数
    1
    已通过
    1
    上传者