P4933 大师 线性dp/枚举

题目大意

给定一个长度为 n 的序列 a[],要求统计所有子序列(可以不连续)使得子序列构成一个等差数列(包括长度为 1 或 2 的子序列)。

核心思路

  1. 动态规划(DP)定义:

    • dp[i][d] 表示以 a[i] 结尾,且公差为 d 的等差数列的子序列个数。

    • 由于公差 d 可以是负数,我们需要进行偏移处理(d + h,其中 h 是一个足够大的数,如 2e4)。

  2. 状态转移:

    • 对于每个 i,遍历所有 j < i,计算 d = a[i] - a[j]

    • 更新 dp[i][d + h]

      • 它可以由 dp[j][d + h] 转移而来(即前面已经有公差为 d 的等差数列)。

      • 同时,a[j] 和 a[i] 本身也能构成一个新的长度为 2 的等差数列,所以 +1

    • 最终答案 ans 要累加所有可能的子序列数量。

  3. 初始条件:

    • 每个单独的元素 a[i] 本身就是一个长度为 1 的等差数列,所以 ans 初始时每次 +1

  4. 模运算:

    • 由于答案可能很大,每次更新 dp 和 ans 时都要取模 998244353


代码注释

#include<bits/stdc++.h>
#define ll long long 
using namespace std;
const int N = 1e3 + 10, mod = 998244353;
ll n, a[N], dp[N][40010];  // dp[i][d] 表示以 a[i] 结尾,公差为 d 的子序列数
int main()
{
    cin >> n;
    for(int i = 1; i <= n; i++) cin >> a[i];
    ll h = 2e4, ans = 0;  // h 是偏移量,防止 d 为负数导致数组越界

    for(int i = 1; i <= n; i++)
    {
        ans++; // 每个单独的元素都是一个长度为 1 的等差数列

        for(int j = 1; j < i; j++)
        {
            int d = a[i] - a[j];  // 计算公差
            dp[i][d + h] += dp[j][d + h] + 1;  // 状态转移
            dp[i][d + h] %= mod;  // 取模防止溢出
            ans = (ans + dp[j][d + h] + 1) % mod;  // 累加所有可能的子序列数
        }
    }
    cout << ans;
    return 0;
}

 

posted @ 2025-06-16 11:30  CRt0729  阅读(8)  评论(0)    收藏  举报