题意
建筑大师最近在跟着数学大师 ljt12138 学数学,今天他学了等差数列,ljt12138 决定给他留一道练习题。
ljt12138 首先建了 n n n 个特斯拉电磁塔,这些电塔排成一排,从左到右依次标号为 1 1 1 到 n n n,第 i i i 个电塔的高度为 h [ i ] h[i] h[i]。
建筑大师需要从中选出一些电塔,然后这些电塔就会缩到地下去。这时候,如果留在地上的电塔的高度,从左向右构成了一个等差数列,那么这个选择方案就会被认为是美观的。
建筑大师需要求出,一共有多少种美观的选择方案,答案模 998244353 998244353 998244353。
注意,如果地上只留了一个或者两个电塔,那么这种方案也是美观的。地上没有电塔的方案被认为是不美观的。
同时也要注意,等差数列的公差也可以为负数。
设 v v v 为最高的电塔高度。
对于前 30 % 30\% 30% 的数据, n ≤ 20 n \le 20 n≤20。
对于前 60 % 60\% 60% 的数据, n ≤ 100 n \le 100 n≤100, v ≤ 2 × 1 0 3 v \le 2 \times 10^3 v≤2×103。
对于另外 20 % 20\% 20% 的数据,所有电塔的高度构成一个等差数列。
对于 100 % 100\% 100% 的数据, n ≤ 1 0 3 n \le 10^3 n≤103, v ≤ 2 × 1 0 4 v \leq2 \times 10^4 v≤2×104。
思路
判断一个数是否在一个等差数列内可以用数列的首项和公差来求——但是这样的时间复杂度是
O
(
n
2
v
)
O(n^2v)
O(n2v)的,无法接受——如何确立一种与
v
v
v无关的状态呢?
我们发现,等差数列的公差就等于相邻两项之差,那么我们可以用枚举最后一项和倒数第二项来求!此时时间复杂度为
O
(
n
3
)
O(n^3)
O(n3),且实际上跑不到那么大(因为第三项下标小于第二项下标,第二项下标小于第一项下标),可以接受。
根据等差数列的公式
a
[
n
]
+
a
[
m
]
=
2
a
[
m
+
n
2
]
a[n] + a[m] = 2a[\frac{m+n}2]
a[n]+a[m]=2a[2m+n]直接暴力枚举,
d
p
[
x
]
[
y
]
dp[x][y]
dp[x][y]表示以最后一项和倒数第二项为等差数列的数列的数量,而因为直接将,可推出状态转移方程
d
p
[
i
]
[
x
]
=
∑
x
=
1
i
−
1
∑
y
=
1
x
−
1
d
p
[
x
]
[
y
]
(
a
[
y
]
+
a
[
i
]
=
2
a
[
x
]
)
dp[i][x] = \sum_{x=1}^{i-1}\sum_{y=1}^{x-1} dp[x][y](a[y] + a[i] = 2a[x])
dp[i][x]=x=1∑i−1y=1∑x−1dp[x][y](a[y]+a[i]=2a[x])
而答案就是
d
p
dp
dp数组值的总和,顺路加一下即可。最后还有只有一个电塔的情况,直接
a
n
s
+
n
ans+n
ans+n即可。记得取模哦!
代码
#include <iostream>
#define piasticOuO 998244353
using namespace std;
int n, h[1005];
int ans, dp[1005][1005]; //dp[x][y]表示最后一项为h[x] 倒数第二项为h[y]的数列的项数
int main() {
cin >> n;
for (int i = 1; i <= n; ++i) {
cin >> h[i];
for (int x = 1; x < i; ++x) {
dp[i][x] = 1;
ans ++;
for (int y = 1; y < x; ++y) {
if (h[y] + h[i] == h[x] * 2) {
dp[i][x] += dp[x][y];
ans += dp[x][y];
dp[i][x] %= piasticOuO;
ans %= piasticOuO;
}
}
}
}
cout << ans + n;
return 0;
}