本文将原题中的 $k$ 记为 $m$。
考虑满足题目要求的序列有什么性质:当一个数特别大而两边特别小的时候是不满足条件的。
准确的说,当 $a_i \gt a_{i-1} + a_{i+1}$ 是不满足的,这是显然的,因为 $a_i$ 这个数要么只能和左边一起 $-1$,要么只能和右边一起。如果左右都分配完它本身仍然 $\gt 0$,那么就无法消除了。
设 $f_{i,j,k}$ 表示写了前 $i$ 个数,$a_{i-1}=j$,$a_i=k$ 的方案数。
这是好转移的,枚举下一次选 $l$ 即可。
这边注意统计答案的时候要特判最后一个数也要满足条件,即 ans += dp[n][i][j]
需要满足条件 $0 \leq j \leq i$,我调试的时候在这里卡了一会儿。
#include <bits/stdc++.h>
using namespace std;
const int N = 415;
int T, n, m, mod, dp[N][N][N];
void solve() {
scanf("%d%d%d", &n, &m, &mod);
for (int i = 0; i <= n; i++)
for (int j = 0; j <= m; j++)
for (int k = 0; k <= m; k++) dp[i][j][k] = 0;
dp[0][0][0] = 1;
for (int i = 0; i < n; i++)
for (int k = 0; k <= m; k++) //i
for (int j = 0; j <= m; j++) { //i-1
if (!dp[i][j][k]) continue;
//满足 0 <= l <= m,且 k <= j+l ----> k-j<=l
for (int l = 0; l <= m; l++) {
if (k <= j + l) (dp[i + 1][k][l] += dp[i][j][k]) %= mod;
}
}
long long ans = 0;
for (int i = 0; i <= m; i++)
for (int j = 0; j <= i; j++) (ans += dp[n][i][j]) %= mod;
printf("%lld\n", ans);
}
int main() {
scanf("%d", &T);
while (T--) solve();
return 0;
}
观察一下复杂度,枚举前 $i$ 个数和 $j,k,l$,复杂度是高贵的 $O(nk^3)$,显然无法通过本题。
观察式子发现对于一个 $l$,满足条件的 $dp_{i,j,k}$ 是一段前缀的形式,那么把 $k \leq j+l$ 和 $0 \leq m$ 移项后组合起来就成了:$\max(0,k-j) \leq l \leq m$。
记录 $sum_{p}$ 表示 $p=\max(0,k-j)$ 的所有 $dp_{i,j,k}$ 之和,然后再做一遍前缀和,转移的时候就不需要枚举 $j$ 了,时间复杂度降低至 $O(nk^2)$。
#include <bits/stdc++.h>
using namespace std;
const int N = 415;
int T, n, m, mod, dp[N][N][N];
int sum[N];
void solve() {
scanf("%d%d%d", &n, &m, &mod);
for (int i = 0; i <= n; i++)
for (int j = 0; j <= m; j++)
for (int k = 0; k <= m; k++) dp[i][j][k] = 0;
dp[0][0][0] = 1;
for (int i = 0; i < n; i++)
for (int k = 0; k <= m; k++) { //i
for (int j = 0; j <= m; j++) sum[j] = 0;
for (int j = 0; j <= m; j++) (sum[max(0, k - j)] += dp[i][j][k]) %= mod;
for (int j = 1; j <= m; j++) (sum[j] += sum[j - 1]) %= mod;
for (int l = 0; l <= m; l++) (dp[i + 1][k][l] += sum[l]) %= mod;
// for (int j = 0; j <= m; j++) { //i-1
// if (!dp[i][j][k]) continue;
// //满足 0 <= l <= m,且 k <= j+l ----> k-j<=l
// for (int l = 0; l <= m; l++) {
// if (max(0, k - j) <= l) (dp[i + 1][k][l] += dp[i][j][k]) %= mod;
// }
// }
}
long long ans = 0;
for (int i = 0; i <= m; i++)
for (int j = 0; j <= i; j++) (ans += dp[n][i][j]) %= mod;
printf("%lld\n", ans);
}
int main() {
scanf("%d", &T);
while (T--) solve();
return 0;
}