解法

概要

bit-DP。先頭にのみ追加する感じで重複を回避。$O(2^N N^2)$。

詳細

制約は $O(2^N N^3)$ のbit-DPを考えろて言っている。 しかし単純にやると、チームの決定の順序が考慮されてしまい重複が発生する。 そこでチームを埋める順番を一意にしてやる。 例えば、チームメンバーの中で添字が一番大きいものをリーダーとし、リーダーの添字の順で追加してやればよい。 チームの集合に対しその構成順序が唯一なので重複なく数えられる。

メモ

  • $O(2^N N^3)$ で実装してしまったがよく見たら i が$1$通りなので自明に $O(2^N N^2)$ にできる

実装

#include <bits/stdc++.h>
#define REP(i, n) for (int i = 0; (i) < (int)(n); ++ (i))
#define REP3(i, m, n) for (int i = (m); (i) < (int)(n); ++ (i))
using ll = long long;
using namespace std;

ll solve(int N, int K, vector<int> const & a) {
    vector<ll> dp(1 << N);
    dp[0] = 1;
    REP3 (s, 1, 1 << N) {
        REP (i, N) if ((s & (1 << i)) and (s ^ (1 << i)) < (1 << i)) {
            dp[s] += dp[s ^ (1 << i)];
            REP (j, i) if (s & (1 << j)) {
                if (a[i] + a[j] <= 2 * K) {
                    dp[s] += dp[s ^ (1 << i) ^ (1 << j)];
                }
                REP (k, j) if (s & (1 << k)) {
                    if (a[i] + a[j] + a[k] <= 3 * K) {
                        dp[s] += dp[s ^ (1 << i) ^ (1 << j) ^ (1 << k)];
                    }
                }
            }
        }
    }
    return dp.back();
}

int main() {
    int n, k; cin >> n >> k;
    vector<int> a(n);
    REP (i, n) cin >> a[i];
    cout << solve(n, k, a) << endl;
    return 0;
}