abc222D 夹在两升序数组之间的升序数组个数

27 阅读1分钟

题面:给定长度为n的两升序数组A[i]和B[i],其中A[i]<=A[i+1]B[i]<=B[i+1],并且0<=A[i]<=B[i]<=3000,找长度为n的数组C[i],满足A[i]<=C[i]<=B[i]。求满足该条件C的个数,结果对998244353取余。

范围:1 <= n <= 3000

分析:设dp[i][j]表示前i个数以j结尾的方案数,那么dp[i][j]=sum(dp[i-1][k]), 0<=k<j,这样递推时间复杂度是O(n^3),会TLE。观察发现这个可以用前缀和优化到O(n^2),另外可以用滚动数组优化空间到O(n)。

#include <bits/stdc++.h>
using namespace std;
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define per(i,a,b) for(int i=b;i>=a;i--)
// mint模板略
const int N = 3000;
int n, A[N+1], B[N+1];
mint dp[N+1], pre[N+1];
mint sum(int l, int r) {
    return l ? pre[r] - pre[l-1] : pre[r];
}
void solve() {
    cin >> n;
    rep(i,1,n) cin >> A[i];
    rep(i,1,n) cin >> B[i];
    dp[0] = 1;
    partial_sum(dp, dp+N+1, pre);
    rep(i,1,n) {
        rep(j,0,N) dp[j] = 0;
        rep(j,A[i],B[i]) {
            dp[j] = sum(A[i-1], j);
        }
        partial_sum(dp, dp+N+1, pre);
    }
    cout << pre[N] << "\n";
}
int main() {
    cin.tie(0)->sync_with_stdio(0);
    int t = 1;
    while (t--) solve();
    return 0;
}

标签:前缀和 线性dp