题解:学而思编程 在两个数列之间

【题目来源】

学而思编程:在两个数列之间

【题目描述】

有两个整数数列 \(a_1,a_2,\dots,a_n\)\(b_1,b_2,\dots,b_n\)。我们的任务是找出满足以下条件的数列 \(c_1,c_2,\dots,c_n\)

(1)对 \(i=1,2,\dots,n,a_i\le c_i\le b_i\)

(2)对 \(i=1,2,\dots,n-1,c_i\le c_{i+1}\)

(3)所有 \(c_i\) 都是整数

满足这些条件的整数数列有多少个?输出答案模 \(998244353\) 的余数。

【输入】

\(1\) 行,\(1\) 个正整数 \(n\)

\(2\) 行,\(n\) 个整数 \(a_1,a_2,\dots,a_n\)

\(3\) 行,\(n\) 个整数 \(b_1,b_2,\dots,b_n\)

【输出】

\(1\) 个整数,输出答案模 \(998244353\) 的余数。

【输入样例】

2
1 1
2 3

【输出样例】

5

【核心思想】

  1. 问题分析:给定两个整数数列 \(a_i\)\(b_i\),要求构造数列 \(c_i\) 满足 \(a_i \leq c_i \leq b_i\)\(c_i \leq c_{i+1}\)。求满足条件的整数数列个数模 \(998244353\)。这是一个线性动态规划 + 前缀和优化问题,关键在于利用非递减约束将状态转移转化为前缀和查询。

  2. 算法选择

    • 线性 DP:以位置 \(i\) 和当前取值 \(j\) 作为状态维度,f[i][j] 表示第 \(i\) 个位置取 \(j\) 时的方案数
    • 前缀和优化:利用前缀和数组 s[i][j] 将"所有 \(c_{i-1} \leq j\) 的方案数之和"从 \(O(\text{值域})\) 降为 \(O(1)\)
  3. 关键步骤

    • 初始化(第 \(1\) 个位置):
      • 遍历 \(j\)\(a_1\)\(b_1\)
        • f[1][j] = 1:第 \(1\) 个位置取 \(j\) 只有 \(1\) 种方案
        • s[1][j] = (s[1][j-1] + f[1][j]) \bmod mod:前缀和数组,\(s[1][j] = \sum_{k=a_1}^{j} f[1][k]\)
    • DP 转移(第 \(2\) 个位置到第 \(n\) 个位置):
      • 遍历 \(i\)\(2\)\(n\)
        • 遍历 \(j\)\(a_i\)\(b_i\)
          • 非递减约束要求 \(c_{i-1} \leq c_i = j\),即前一位取值范围为 \([a_{i-1}, \min(b_{i-1}, j)]\)
          • f[i][j] = s[i-1][\min(b_{i-1}, j)] - s[i-1][a_{i-1} - 1]:利用前缀和 \(O(1)\) 查询合法方案数
          • f[i][j] < 0,加 mod 修正负数
          • s[i][j] = (s[i][j-1] + f[i][j]) \bmod mod:维护当前层前缀和
    • 统计答案
      • sum = \sum_{j=a_n}^{b_n} f[n][j] \bmod mod:第 \(n\) 个位置所有合法取值的方案数之和
    • 输出 sum
  4. 时间/空间复杂度

    • 时间复杂度:\(O(n \times V)\)\(V\) 为值域大小(最大 \(b_i\)),每个状态 \(O(1)\) 转移
    • 空间复杂度:\(O(n \times V)\),DP 数组 f[n][V] 和前缀和数组 s[n][V]
  5. 前缀和优化的核心思想

    • 状态设计f[i][j] 表示第 \(i\) 个位置取值为 \(j\) 时的方案数,状态维度为位置和取值
    • 约束转化:非递减条件 \(c_{i-1} \leq c_i\) 转化为"前一位取值不超过当前值",即 \(\sum_{k=a_{i-1}}^{\min(b_{i-1}, j)} f[i-1][k]\)
    • 前缀和降维:前缀和数组 s[i][j] = \sum_{k=a_i}^{j} f[i][k],将区间求和从 \(O(\text{区间长度})\) 优化到 \(O(1)\)
    • 值域压缩处理:通过 \min(b_{i-1}, j) 处理前一位上界与当前值的大小关系,确保查询范围合法
    • 模运算处理:减法后及时加模数修正负数,保证结果非负
    • 适用于"序列构造 + 相邻约束 + 计数"类问题,前缀和可将 \(O(n \times V^2)\) 优化至 \(O(n \times V)\)

【解题思路】

【算法标签】

线性DP-一维

【代码详解】

#include <bits/stdc++.h>
using namespace std;
const int mod = 998244353;
int a[3005], b[3005], n;
int f[3005][3005], s[3005][3005];

int main()
{
    scanf("%d", &n);
    for (int i = 1; i <= n; i++)
        scanf("%d", &a[i]);  // 读取每个位置的下界
    for (int i = 1; i <= n; i++)
        scanf("%d", &b[i]);  // 读取每个位置的上界

    // 初始化第一个位置
    for (int j = a[1]; j <= b[1]; j++) {
        f[1][j] = 1;  // 第一个位置选j只有1种方案
        s[1][j] = (s[1][j-1] + f[1][j]) % mod;  // 前缀和
    }

    // DP计算
    for (int i = 2; i <= n; i++) {
        for (int j = a[i]; j <= b[i]; j++) {
            // 计算f[i][j]:第i个位置选j的方案数
            if (j >= a[i-1]) {  // 满足非递减条件
                f[i][j] = (f[i][j] + s[i-1][min(b[i-1], j)] - s[i-1][a[i-1]-1]) % mod;
                // 减去可能的负数
                if (f[i][j] < 0) f[i][j] += mod;
            }
            // 计算前缀和
            s[i][j] = (s[i][j-1] + f[i][j]) % mod;
        }
    }

    // 计算结果
    int sum = 0;
    for (int j = a[n]; j <= b[n]; j++)
        sum = (sum + f[n][j]) % mod;

    printf("%d\n", sum);
    return 0;
}

【运行结果】

2
1 1
2 3
5
posted @ 2026-07-04 12:19  团爸讲算法  阅读(5)  评论(0)    收藏  举报