以下是 LeetCode 3395. 唯一中间众数子序列 I 的 Rust 实现:

```rust
use std::collections::HashMap;

impl Solution {
    pub fn subsequences_with_middle_mode(nums: Vec<i32>) -> i32 {
        const MOD: i64 = 1_000_000_007;
        let n = nums.len();
        
        // 总方案数 = C(n, 5)
        let mut ans: i64 = (n as i64) * ((n - 1) as i64) * ((n - 2) as i64) 
                         * ((n - 3) as i64) * ((n - 4) as i64) / 120;
        
        // 后缀频率:每个数字在后缀中的出现次数
        let mut suf: HashMap<i32, i32> = HashMap::new();
        for &num in &nums {
            *suf.entry(num).or_insert(0) += 1;
        }
        
        // 前缀频率:每个数字在前缀中的出现次数(初始为0)
        let mut pre: HashMap<i32, i32> = suf.keys().map(|&k| (k, 0)).collect();
        
        // cp = sum of C(pre[y], 2) for all y
        let mut cp: i64 = 0;
        // cs = sum of C(suf[y], 2) for all y
        let mut cs: i64 = suf.values().map(|&c| (c as i64) * ((c - 1) as i64) / 2).sum();
        // ps = sum of pre[y] * suf[y] for all y
        let mut ps: i64 = 0;
        // p2s = sum of pre[y]^2 * suf[y] for all y
        let mut p2s: i64 = 0;
        // ps2 = sum of pre[y] * suf[y]^2 for all y
        let mut ps2: i64 = 0;
        
        // 枚举中间元素 x(位置 i 作为子序列的第3个元素)
        for i in 0..n - 2 {
            let x = nums[i];
            let left = i as i64;           // x 左侧的元素个数
            let right = (n - 1 - i) as i64; // x 右侧的元素个数
            
            // 将 x 从后缀移到"当前处理"
            *suf.get_mut(&x).unwrap() -= 1;
            let sx = suf[&x] as i64;        // x 在后缀中的出现次数
            let px = pre[&x] as i64;        // x 在前缀中的出现次数
            
            // 更新后缀相关统计量(去掉 x 的旧贡献)
            cs -= sx;                        // C(sx+1, 2) - C(sx, 2) = sx
            ps -= px;                        // pre[x] * suf[x] 的变化
            p2s -= px * px;                 // pre[x]^2 * suf[x] 的变化
            ps2 -= (2 * sx + 1) * px;       // pre[x] * suf[x]^2 的变化
            
            // 减去不满足条件的子序列数(正难则反)
            
            // 情况1: x 只出现1次(仅作为中间元素)
            // 左右各选2个非x元素
            ans -= (left - px) * (left - px - 1) / 2 
                 * (right - sx) * (right - sx - 1) / 2;
            
            // 情况2: x 出现2次,某个 y 也出现2次(y 的两次都在左侧)
            ans -= (cp - px * (px - 1) / 2) * sx * (right - sx);
            
            // 情况3: x 出现2次,某个 y 也出现2次(y 的两次都在右侧)
            ans -= (cs - sx * (sx - 1) / 2) * px * (left - px);
            
            // 情况4: x 出现2次,某个 y 也出现2次(y 左右各一次,x 来自左侧)
            ans -= ((ps - px * sx) * (right - sx) 
                  - (ps2 - px * sx * sx)) * px;
            
            // 情况5: x 出现2次,某个 y 也出现2次(y 左右各一次,x 来自右侧)
            ans -= ((ps - px * sx) * (left - px) 
                  - (p2s - px * px * sx)) * sx;
            
            // 更新前缀相关统计量(加上 x 的新贡献)
            *pre.get_mut(&x).unwrap() += 1;
            cp += px;                        // C(px+1, 2) - C(px, 2) = px
            ps += sx;                        // pre[x] * suf[x] 的变化
            ps2 += sx * sx;                 // pre[x] * suf[x]^2 的变化
            p2s += (2 * px + 1) * sx;       // pre[x]^2 * suf[x] 的变化
        }
        
        ((ans % MOD + MOD) % MOD) as i32
    }
}
```

思路说明

核心思想:正难则反(容斥原理)

总方案数 = 所有长度为 5 的子序列数 `C(n, 5)`,减去不满足"唯一中间众数"条件的子序列数。

变量定义(前缀-后缀统计量):

变量    含义    
`cp`    `Σ C(pre[y], 2)` — 前缀中所有数字的两两组合数之和    
`cs`    `Σ C(suf[y], 2)` — 后缀中所有数字的两两组合数之和    
`ps`    `Σ pre[y] × suf[y]` — 前缀后缀配对数之和    
`p2s`    `Σ pre[y]² × suf[y]`    
`ps2`    `Σ pre[y] × suf[y]²`    

五种不满足条件的情况(`x` 为中间元素):

情况    描述    计数公式    
1    `x` 只出现1次,其余4个元素任意    `C(L-px, 2) × C(R-sx, 2)`    
2    `x` 出现2次,某 `y` 也出现2次(`y` 的两次都在左侧)    `(cp - C(px,2)) × sx × (R-sx)`    
3    `x` 出现2次,某 `y` 也出现2次(`y` 的两次都在右侧)    `(cs - C(sx,2)) × px × (L-px)`    
4    `x` 出现2次,某 `y` 也出现2次(`y` 左右各一次,`x` 来自左侧)    `((ps-px×sx)(R-sx) - (ps2-px×sx²)) × px`    
5    `x` 出现2次,某 `y` 也出现2次(`y` 左右各一次,`x` 来自右侧)    `((ps-px×sx)(L-px) - (p2s-px²×sx)) × sx`    

复杂度:时间 O(n),空间 O(u)(u 为不同数字个数,u ≤ n ≤ 1000)。

 

更多推荐