Dark Horse
有 \(2^n\) 个人打锦标赛,他们的过程是随机一个排列,然后按照这个排列站好。每轮是第 \(2i − 1\) 个人和第 \(2i\) 的人比赛,败者淘汰。
你是 \(1\) 号选手,你碰到 \(a_1, a_2,\dots, a_m\) 会输,碰到剩下的会赢。如果比赛和你无关,那么编号小的赢。
求有多少个排列,能够使你最后赢。答案对 \(10^9 + 7\) 取模。
\(1 ≤ n ≤ 16, 0 ≤ m ≤ 16, 2 ≤ a_i ≤ 2^n\)。
题解
对做题的人而言,分析法比解析法更有逻辑。
首先可以发现这棵二叉树每个非叶子节点都可以交换左右儿子。所以我们可以钦定 \(1\) 号选手站在 \(1\) 号位置来找本质不同的方案。
既然要 \(1\) 号胜出,那么我们就来看看 \(1\) 号最终会跟哪些人比赛。显然有 \(\{2\},\{3,4\},\{5,6,7,8\}\dots\) 这些位置上的最小值。写成通项:
\[ P_k=\{p_{2^{k-1}+1},\dots,p_{2^k}\},k\in[1,n] \]
那么我们要求 \(\forall k\in [1,n],\min P_k\notin A\)。
“最小值不属于 \(A\)”的方案太多,但是“最小值属于 \(A\)”却给了一个最小值是某个 \(a_i\) 的限制。所以这个反面更可做。
考虑容斥。
\[ \frac{ans}{2^n}=(2^n-1)!+\sum_{S\subseteq \{P\}}(-1)^{|S|}F(S) \]
其中 \(F(S)\) 表示 \(S\) 中的 \(P\) 均满足 \(\min P\in A\),而非 \(S\) 中的 \(P\) 无所谓的方案数。非 \(S\) 中的 \(P\) 的方案数可以在 \(S\) 中的 \(P\) 确定后用排列数搞定,所以进一步的,
\[ F(S)=(2^n-1-\sum_{P\in S}|P|)!G(S) \]
其中 \(G(S)\) 表示给 \(S\) 中的 \(P\) 分配选手使得他们满足条件的方案数。
显然若 \(a_i<a_j\),那么最小值钦定为 \(a_j\) 的 \(P\) 能够选的其他球包含于最小值钦定为 \(a_i\) 的 \(P\) 能选的球。按 \(A\) 中元素从大到小DP,记 \(G(i,S)\) 表示做到 \(a_i\) 被钦定的集合是 \(S\) 时的方案数,对于每一个 \(a_i\),我们做如下两个决策之一:
不把 \(a_i\) 作为某一集合钦定的最小值。
钦定 \(a\) 作为 \(P_k\) 的最小值,并再选取大于 \(a\) 且未被选取的 \(2^{k-1}-1\) 个数。
最终答案为
\[ ans=2^n\left((2^n-1)!+\sum_{S\subseteq \{P\}} (-1)^{|S|}\times (2^n-\sum_{P\in S}|P|)!\times G(1,S)\right) \]
时间复杂度 \(O(2^nnm)\)。
CO int N=20,S=65536+10;
int a[N];
int fac[S],ifac[S],dp[N][S];
IN int binom(int n,int m){
return mul(fac[n],mul(ifac[m],ifac[n-m]));
}
int main(){
int n=read<int>(),m=read<int>();
for(int i=m;i>=1;--i) read(a[i]);
int all=1<<n;
fac[0]=1;
for(int i=1;i<=all;++i) fac[i]=mul(fac[i-1],i);
ifac[all]=fpow(fac[all],mod-2);
for(int i=all-1;i>=0;--i) ifac[i]=mul(ifac[i+1],i+1);
dp[0][0]=1;
for(int i=1;i<=m;++i)for(int s=0;s<all;++s){
int left=all-a[i]+1-s;
cadd(dp[i][s],dp[i-1][s]);
for(int j=0;j<n;++j)if(~s>>j&1){
if(left<1<<j) break;
cadd(dp[i][s|1<<j],mul(dp[i-1][s],
mul(binom(left-1,(1<<j)-1),fac[1<<j])));
}
}
int ans=fac[all-1];
for(int s=1;s<all;++s){
int sum=mul(dp[m][s],fac[all-1-s]);
cadd(ans,__builtin_popcount(s)&1?mod-sum:sum);
}
cmul(ans,all);
printf("%d\n",ans);
return 0;
}