「解题报告」[省选联考 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;
}
posted @ 2023-02-13 20:42  APJifengc  阅读(48)  评论(0编辑  收藏  举报