ARC 093 F

传送门

题目大意

有$2^{n\le16}$名选手,编号为$1$至$2^n$。现在这$2^n$名选手将进行$n$轮淘汰赛,决出胜者。

若$x<y$,则$x$能够战胜$y$。但有$m\le16$个例外,$1$号选手会输给这$m$个选手。

问有多少种选手的排列方式使得$1$号选手取得胜利。

解析

假设选手的排列是$p$,我们考虑$1$会和哪些选手打。

显然,他会先和$p_2$打,然后和$min{p_3,p_4}$打,然后和$min{p_5,p_6,p_7,p_8}$打,也就是会和$S_{k\in[1,n]}={p_{2^{k-1}+1},…,p_{2^k}}$打。

那么显然方案数等于上述$n$个集合中每个集合的最小值都不是给定的$m$个人的方案数。

考虑容斥。钦定哪些块的最小值一定是给定的$m$个人,那么就可以状压一下。设方案数为$f(S)$,那么答案就是$\sum (-1)^{|S|}f(S)$。

怎么求$f(S)$?这个就可以状压dp一下了。

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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
#define  REP(i, s, e) for (int i = s; i <= e; i++)
#define DREP(i, s, e) for (int i = s; i >= e; i--)
#define DEBUG fprintf(stderr, "Passing [%s] in Line %d\n", __FUNCTION__, __LINE__)

#include <algorithm>
#include <iostream>
#include <cstdio>
using namespace std;
const int MOD = 1e9 + 7, maxn = 20, maxN = 1 << maxn;
int bin[maxn], fac[maxN], inv[maxN], Inv[maxN];
inline int C(int n, int m) {return n < m ? 0 : 1ll * fac[n] * Inv[m] % MOD * Inv[n-m] % MOD;}

int n, m;
long long ans;
int a[maxn], dp[maxn][maxN], cnt[maxN];

int power_pow(long long base, int b)
{
long long ans(1);
while(b)
{
if (b & 1) (ans *= base) %= MOD;
(base *= base) %= MOD;
b >>= 1;
}
return (ans + MOD) % MOD;
}

signed main()
{
#ifdef CraZYali
freopen("F.in", "r", stdin);
freopen("F.out", "w", stdout);
#endif
cin >> n >> m;
cnt[0] = fac[0] = bin[0] = Inv[0] = inv[0] = inv[1] = 1;
REP(i, 1, n) bin[i] = bin[i-1]<<1;
REP(i, 1, bin[n]) fac[i] = 1ll * fac[i-1] * i%MOD;
Inv[bin[n]] = power_pow(fac[bin[n]], MOD - 2);
DREP(i, bin[n]-1, 1) Inv[i] = 1ll * Inv[i+1] * (i+1) % MOD;
REP(i, 1, bin[n]) inv[i] = 1ll * Inv[i] * fac[i-1] % MOD;
REP(i, 1, bin[n]) cnt[i] = (i&1) ? MOD - cnt[i>>1]:cnt[i>>1];
REP(i, 1, m) scanf("%d", a + i);
sort(a + 1, a + 1 + m);
dp[m+1][0] = 1;
DREP(i, m, 1)
REP(S, 0, bin[n]-1)
if(dp[i+1][S])
{
(dp[i][S] += dp[i+1][S]) %= MOD;
int p = bin[n] - S - 1;
REP(j, 0, n)
if(!(S & (1<<j)))
(dp[i][S | (1<<j)] += 1ll * C(p - a[i] + 1, (1<<j) - 1) * dp[i+1][S] % MOD * fac[1<<j] % MOD) %= MOD;
}
REP(i, 0, bin[n]-1)
(ans += 1ll * dp[1][i] * fac[bin[n]-1-i] % MOD * cnt[i] % MOD) % MOD;
printf("%lld\n", (1ll * ans * bin[n] % MOD + MOD) % MOD);
return 0;
}
# 容斥

评论

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×