雀食是DP好题,DP玄学优化呜呜。
题目链接:codeforces 1188C
题目思路:
美丽值与顺序无关,故对数组排序,子序列的美丽值就是所有相邻数的差值的最小值。
设美丽值为
x
x
x 的子序列个数为 cnt[x]
,那么美丽值为1的子序列个数为 cnt[1]
,它对答案的贡献为 cnt[1]*1
,相似的,美丽值为2的子序列对答案的贡献为 cnt[2]*2
,差值的最大值为
1
0
5
10^5
105,故最后的答案为:
a
n
s
=
∑
i
=
1
1
0
5
c
n
t
[
i
]
∗
i
ans=\sum_{i = 1}^{10^5}cnt[i]*i
ans=i=1∑105cnt[i]∗i
不难发现答案就是 cnt
的后缀和 suf
的和,suf[i]
表示美丽值不小于
i
i
i 的方案数。于是,我们可以枚举美丽值
x
x
x。令 dp[i][j]
为最后一位为
i
i
i,长度为
j
j
j,差值不小于
x
x
x 的子序列。定义状态转移方程:
d
p
[
i
]
[
j
]
=
∑
d
p
[
l
]
[
j
−
1
]
,
a
[
i
]
−
a
[
l
]
≥
x
dp[i][j] = \sum dp[l][j-1], a[i]-a[l] \ge x
dp[i][j]=∑dp[l][j−1],a[i]−a[l]≥x
因为
l
l
l 是递增的,所以用前缀和求这个
s
u
m
sum
sum,最后算出的 dp[n][k]
就是美丽值不小于
x
x
x 的方案数。时间复杂度为
O
(
n
∗
k
∗
a
[
n
]
)
O(n*k*a[n])
O(n∗k∗a[n])。
这里优化参考dalao,具体做法是优化 dp 范围。长度为 k k k 的子序列最大差值为 ( k − 1 ) ∗ x (k-1)*x (k−1)∗x,而美丽值最大值是 a [ n ] a[n] a[n],故现在只要求 x ≤ a [ n ] / ( k − 1 ) x\le a[n]/(k-1) x≤a[n]/(k−1) 的范围即可,时间复杂度为 O ( n ∗ a [ n ] ) O(n*a[n]) O(n∗a[n])。
参考代码:
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
const int N = 1e3 + 10;
const int mod = 998244353;
ll a[N], dp[N][N], pre[N][N];
int main() {
int n, k;
cin >> n >> k;
for (int i = 1; i <= n; i++) cin >> a[i];
sort(a+1, a+1+n);
a[0] = -0x3f3f3f3f;
ll ans = 0;
for (int x = 1; x * (k-1) <= a[n]; x++) {
dp[0][0] = 1, pre[0][0] = 1;
int now = 0, res = 0;
for (int i = 1; i <= n; i++) {
while (x <= a[i] - a[now]) now++;
for (int j = 0; j <= k; j++) {
if (j) dp[i][j] = pre[now-1][j-1];
pre[i][j] = (pre[i-1][j] + dp[i][j]) % mod;
}
res = (res + dp[i][k]) % mod;
}
ans = (ans + res) % mod;
}
cout << ans << endl;
return 0;
}