Loading [MathJax]/jax/element/mml/optable/SuppMathOperators.js

[AtCoder ARC093F]Dark Horse

题目大意:2n个人,每相邻的两个人比赛一次。令两个人的编号为a,b(a,若a\neq 1,则a的人获胜;否则若b\in Sb获胜,不然1获胜。钦定1获胜,问可以的开始的顺序的方案数

题解:状压DP,令开始的第i位的人的编号为p_i,发现到只有\min\limits_{i\in[2^{j-1}+1,2^j]}\{p_i\}(1\leqslant j\leqslant n)的人会和1打,考虑容斥,令f_{i,j}为到了要放S中的第i个人,现在第k个段([2^{k-1}+1,2^k])中的最小值在S中的状态为1<<k \& j,时可以战胜1的方案数。(发现一个很优美的东西,j==已经放置的人数

卡点:

C++ Code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#include <cstdio>
#define N 1 << 16 | 3
const int mod = 1000000007;
int n, m, s[20];
long long fac[N], inv[N];
long long f[17][N], ans, U;
void update(long long &x, long long y) {if ((x += y) >= mod) x -= mod;}
long long C(long long a, long long b) {
    if (a < b) return 0;
    return fac[a] * inv[b] % mod * inv[a - b] % mod;
}
int main () {
    scanf("%d%d", &n, &m); U = 1 << n;
    for (int i = 1; i <= m; i++) scanf("%d", s + m - i);
    fac[0] = fac[1] = inv[0] = inv[1] = 1;
    for (int i = 2; i < U; i++) {
        fac[i] = fac[i - 1] * i % mod;
        inv[i] = inv[mod % i] * (mod - mod / i) % mod;
    }
    for (int i = 2; i < U; i++) inv[i] = inv[i - 1] * inv[i] % mod;
    f[0][0] = 1;
    for (int i = 0; i < m; i++) {
        for (int j = 0; j < U; j++) {
            update(f[i + 1][j], f[i][j]);
            for (int k = 0; k < n; k++) {
                if (!(j & (1 << k))) update(f[i + 1][j | 1 << k], f[i][j] * fac[1 << k] % mod * C(U - j - s[i], (1 << k) - 1) % mod);
            }
        }
    }
    for (int i = 0; i < U; i++) {
        long long tmp = f[m][i] * fac[U - i - 1] % mod;
        update(ans, __builtin_parity(i) ? (mod - tmp) : tmp);
    }
    printf("%lld\n", ans * U % mod);
    return 0;
}

 

posted @   Memory_of_winter  阅读(177)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示