2025CSP-S模拟赛17 比赛总结
2025CSP-S模拟赛17
T1 zzy 的金牌
考虑如何判断一个可重集 \(\{s_1,s_2,\dots s_n\}\) 是否可能成为答案,这个可以贪心:将 \(s\) 与 \(a\) 分别从小到大排序后,若 \(\forall i,s_i \geq a_i\) 且 \(\sum s - \sum a=k\) 则合法。于是乎所求即为 \(b_1,b_2,\dots b_n\) 的数量,其中 \(b\) 满足 \(b_i+a+i \geq b_{i-1}+a_{i-1}\) 且 \(\sum b_i=k\)。
考虑设计 dp:\(f_{i,j,k}\) 表示 \(b\) 的前 \(i\) 项有多少种填法满足 \(b_i=j\) 且 \((\sum_{x\le i}b_x)=k\)。转移很简单,\(f_{i,j,k}\rightarrow f_{i+1,l,k+l}(l+a_{i+1} \geq j+a_i)\)。
此时的时间复杂度来到 \(O(nk^3)\)。
考虑令 \(g_{i,k-j,j}=f_{i,j,k}\),则 \(g_{i,k-j,j}\rightarrow g_{i+1,k,l}(l+a_{i+1} \geq j+a_i)\)。容易发现这是对 \(g_{i+1,k}\) 的一个后缀加,差分维护即可。
时间复杂度 \(O(nk^2)\)。
#include <bits/stdc++.h>
#define int long long
#define ull unsigned long long
using namespace std;
int read() {
int x = 0; char ch = getchar();
while (ch < '0' || ch > '9') ch = getchar();
while (ch >= '0' && ch <= '9') {
x = (x << 1) + (x << 3) + (ch ^ 48);
ch = getchar();
}
return x;
}
const int P = 131;
const int MOD = 998244353;
const int N = 300 + 10;
int n, kk, a[N];
int f[N][N][N];
signed main() {
n = read(), kk = read();
for (int i = 1; i <= n; i++) {
a[i] = read();
}
sort(a + 1, a + 1 + n);
f[0][0][0] = 1;
for (int i = 0; i <= n; i++) {
for (int k = 0; k <= kk; k++) {
for (int j = 0; j <= k; j++) {
if (!f[i][k - j][k]) continue;
int l = max(0ll, a[i] + j - a[i + 1]), r = kk - k;
if (l <= r) {
(f[i + 1][k][k + l] += f[i][k - j][k]) %= MOD;
((f[i + 1][k][k + r + 1] -= f[i][k - j][k]) += MOD) %= MOD;
}
}
for (int j = 0; j <= kk; j++) {
(f[i + 1][k][j + 1] += f[i + 1][k][j]) %= MOD;
}
}
}
int ans = 0;
for (int j = 0; j <= kk; j++) {
(ans += f[n][j][kk]) %= MOD;
}
printf("%lld\n", ans);
return 0;
}

浙公网安备 33010602011771号