简单的容斥DP。
显然所在位置是不重要的,不妨设。那么我们可以发现要比赛的人是另外个大小分别为,,,的有序集合的最小值。能获胜即等价于这些集合的最小值均不为。
直接算不好做,考虑容斥,钦定若干个为某些集合的最小值。按从大往小考虑,设表示考虑了~ ,的二进制位对应大小的集合最小值已经被钦定为~中的人的贡献和,转移简单枚举一下是否钦点为某个集合的最小值,是的话系数是组合数,容易计算。
时间复杂度。
#include <bits/stdc++.h>
#define MOD 1000000007
using namespace std;
typedef long long ll;
ll pow_mod(ll x,int k) {
ll ans=1;
while (k) {
if (k&1) ans=ans*x%MOD;
x=x*x%MOD;
k>>=1;
}
return ans;
}
inline void add(int &x,ll y) {
x=(x+y)%MOD;
}
ll facd[70005],facv[70005];
void pre(int n) {
facd[0]=1;
for(int i=1;i<=n;i++) facd[i]=facd[i-1]*i%MOD;
facv[n]=pow_mod(facd[n],MOD-2);
for(int i=n-1;i>=0;i--) facv[i]=facv[i+1]*(i+1)%MOD;
}
inline ll C(int n,int m) {
return (n<m)?0:facd[n]*facv[m]%MOD*facv[n-m]%MOD;
}
int f[17][1<<16],num[17];
int dp(int n,int m) {
f[0][0]=1;
for(int i=1;i<=m;i++)
for(int j=0;j<(1<<n);j++)
if (f[i-1][j]) {
add(f[i][j],f[i-1][j]);
for(int k=0;k<n;k++)
if (!((j>>k)&1)&&j+(1<<k)<=num[i])
add(f[i][j^(1<<k)],(MOD-f[i-1][j])*C(num[i]-j-1,(1<<k)-1));
}
int ans=0;
for(int i=0;i<(1<<n);i++)
if (f[m][i]) {
int s=(1<<n)-i-1;
ll v=f[m][i];
for(int j=0;j<n;j++)
if (!((i>>j)&1)) {
v=v*C(s,1<<j)%MOD;
s-=(1<<j);
}
add(ans,v);
}
for(int i=0;i<n;i++) ans=ans*facd[1<<i]%MOD;
return ans;
}
int main() {
int n,m;
scanf("%d%d",&n,&m);
pre(1<<n);
for(int i=1;i<=m;i++) {
int x;
scanf("%d",&x);
num[i]=(1<<n)-x+1;
}
reverse(num+1,num+m+1);
printf("%lld\n",(ll)dp(n,m)*(1<<n)%MOD);
return 0;
}
来源:CSDN
作者:mayaohua2003
链接:https://blog.csdn.net/qq_38609262/article/details/103484914