「解题报告」[省选联考 2021 A/B 卷] 滚榜
简单题。
脑子不转怎么办啊。
先分析限制:假如上一个队是 \(i\),这个队是 \(j\),那么合法的 \(b_j\) 需要满足 \(b_i \le b_j\) 且 \(a_i + b_i + [i < j] \le a_j + b_j\),即 \(b_j \ge b_i + \max(a_i - a_j + [i < j], 0)\)。
发现每次 \(b_j - b_i\) 与 \(b_i\) 是无关的,所以我们可以先提前计算出 \(b_i\) 的贡献,把横向贡献改成纵向贡献,这样就可以不记录 \(b_i\) 了,这样我们只需要记录当前选了哪些数、上一个选的什么、当前 \(\sum b\),容易转移,复杂度 \(O(2^n n^2 m)\)。
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 13, MAXM = 505, P = 998244353;
int n, m, a[MAXN];
long long f[1 << MAXN][MAXN][MAXM];
int main() {
scanf("%d%d", &n, &m);
for (int i = 0; i < n; i++) {
scanf("%d", &a[i]);
}
for (int i = 0; i < n; i++) {
int c = 0;
for (int j = 0; j < n; j++) if (i != j) {
c = max(c, a[j] - a[i] + (i > j));
}
if (c * n <= m) {
f[1 << i][i][c * n] = 1;
}
}
for (int s = 1; s < (1 << n); s++) {
int c = __builtin_popcount(s);
for (int i = 0; i < n; i++) if (s >> i & 1) {
for (int j = 0; j <= m; j++) if (f[s][i][j]) {
for (int k = 0; k < n; k++) if (!(s >> k & 1) && j + (n - c) * max(a[i] - a[k] + (i < k), 0) <= m) {
f[s | 1 << k][k][j + (n - c) * max(a[i] - a[k] + (i < k), 0)] += f[s][i][j];
}
}
}
}
long long ans = 0;
for (int i = 0; i <= m; i++) {
for (int j = 0; j < n; j++) {
ans += f[(1 << n) - 1][j][i];
}
}
printf("%lld\n", ans);
return 0;
}