P4933 大师 线性dp/枚举
题目大意
给定一个长度为 n 的序列 a[],要求统计所有子序列(可以不连续)使得子序列构成一个等差数列(包括长度为 1 或 2 的子序列)。
核心思路
-
动态规划(DP)定义:
-
dp[i][d]表示以a[i]结尾,且公差为d的等差数列的子序列个数。 -
由于公差
d可以是负数,我们需要进行偏移处理(d + h,其中h是一个足够大的数,如2e4)。
-
-
状态转移:
-
对于每个
i,遍历所有j < i,计算d = a[i] - a[j]。 -
更新
dp[i][d + h]:-
它可以由
dp[j][d + h]转移而来(即前面已经有公差为d的等差数列)。 -
同时,
a[j]和a[i]本身也能构成一个新的长度为 2 的等差数列,所以+1。
-
-
最终答案
ans要累加所有可能的子序列数量。
-
-
初始条件:
-
每个单独的元素
a[i]本身就是一个长度为 1 的等差数列,所以ans初始时每次+1。
-
-
模运算:
-
由于答案可能很大,每次更新
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; }

浙公网安备 33010602011771号