Counting Is Fun (Easy Version)
题意
一个长度为 m m m的数组b成为是good的,当且仅当通过执行下面这个操作若干次可以让数组b中所有元素变成 0 0 0:
- 选择两个不同的下标 l l l和 r r r,其中 ( 1 ≤ l < r ≤ m ) (1 \leq l < r \leq m) (1≤l<r≤m),然后 b i ( l ≤ i ≤ r ) b_{i}(l \leq i \leq r) bi(l≤i≤r)所有元素减 1 1 1
给定n,k,p,n为数组长度,k为数组中每个元素的取值范围为 [ 0 , k ] [0,k] [0,k],p为模数,求在 ( k + 1 ) n (k+1)^{n} (k+1)n个数组里有多少个数组是good的
分析
首先观察到 l l l是不能等于 r r r的,即一次操作最少有两个元素进行减1,所以差分数组就需要选择一对 ( i , j ) (i,j) (i,j),其中 ( i + 2 ≤ j ) (i+2 \leq j) (i+2≤j),令原数组为 a a a,差分数组为 b b b,一次操作也就是找到一对 ( i , j ) (i,j) (i,j), b i − 1 b_{i}-1 bi−1, b j + 1 b_{j}+1 bj+1,要让所有元素都为0,差分数组所有元素也得是0,所以充要条件就是, b i + ∑ j = 1 i − 2 b j ≥ 0 ⇔ a i − a i − 1 + a i − 2 ≥ 0 b_{i}+\sum_{j=1}^{i-2} b_{j} \geq 0 \Leftrightarrow a_{i}-a_{i-1}+a_{i-2} \geq 0 bi+∑j=1i−2bj≥0⇔ai−ai−1+ai−2≥0,若某个时刻 b i < 0 b_{i}<0 bi<0而 b 1 . . . b i − 2 > 0 b_{1}...b_{i-2}>0 b1...bi−2>0一定是不合法的。
那么就可以用设计dp状态统计方案数了,根据数据范围,最大可以到 n 3 n^{3} n3,设 d p i , j , k dp_{i,j,k} dpi,j,k为到第 i i i个为止, a i − 1 = j a_{i-1}=j ai−1=j, a i = k a_{i}=k ai=k时符合条件的方案数,转移就是 d p i , j , k ← ( d p i , j , k + d p i − 1 , l , j ) % p dp_{i,j,k} \leftarrow (dp_{i,j,k}+dp_{i-1,l,j})\%p dpi,j,k←(dpi,j,k+dpi−1,l,j)%p,其中 a i − 2 = l , k ≥ m a x ( 0 , j − l ) a_{i-2}=l,k \geq max(0,j-l) ai−2=l,k≥max(0,j−l),由此可以发现枚举 i , j , k i,j,k i,j,k就已经时 n 3 n^{3} n3级别,再考虑优化转移,发现可以考虑后缀和使得转移可以 O ( 1 ) O(1) O(1)进行,因为转移的时候考虑的都是 k ≥ m a x ( 0 , j − l ) k \geq max(0,j-l) k≥max(0,j−l)的部分
代码
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
int dp1[410][410][410], dp2[410][410][410];
void Solve() {
int n, k, mod;
cin >> n >> k >> mod;
dp1[0][0][0] = 1;
for (int i = 0; i <= k; i++) {
dp2[0][i][0] = 1;
}
for (int i = 1; i <= n; i++) {
for (int j = 0; j <= k; j++) {
for (int l = 0; l <= k; l++) {
if (i == 1) {
for (int ll = 0; ll <= k; ll++) {
dp1[i][j][l] = (dp1[i - 1][ll][j] + dp1[i][j][l]) % mod;
}
} else {
dp1[i][j][l] = (dp1[i][j][l] + dp2[i - 1][max(0, j - l)][j]) % mod;
}
}
}
for (int j = k; j >= 0; j--) {
for (int l = 0; l <= k; l++) {
dp2[i][j][l] = dp1[i][j][l];
if (j + 1 <= k) {
dp2[i][j][l] = (dp2[i][j][l] + dp2[i][j + 1][l]) % mod;
}
}
}
}
LL ans = 0;
for (int i = 0; i <= k; i++) {
for (int j = 0; j <= i; j++) {
ans = (ans + dp1[n][i][j]) % mod;
}
}
cout << ans << '\n';
for (int i = 0; i <= n; i++) {
for (int j = 0; j <= k; j++) {
for (int l = 0; l <= k; l++) {
dp1[i][j][l] = dp2[i][j][l] = 0;
}
}
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int T;
cin >> T;
while (T--) {
Solve();
}
return 0;
}