传送门
思路:
考虑理想情况,所有魔法师的区间都不相交,那么总长度减去所有区间长度即为多余格子数,可以直接组合计数了。
然而,事实上,对于2个相邻的区间,不妨假设左边的区间更小,那么两个区间之间就有【左边区间的右半部分】的格子可以重叠。因此,相应的长度就可以作为额外的多余格子。
于是问题转化为统计每个多余格子数目分别对应了多少种排列。
考虑DP。
由于额外的多余的格子取决于两个相邻区间的大小,所以我们把魔法师按攻击范围从大到小排序,然后逐个插入,把这些还会插入更小魔法师的位置称为空位。
设:dp[i][j][s]表示插入了前i个魔法师、目前有j个空位、提供了s个额外的格子。
然后
d
p
dp
dp即可。
代码:
#include<bits/stdc++.h>
#define ri register int
using namespace std;
typedef long long ll;
const int mod=1e9+7,inv2=(mod+1)>>1;
inline int add(const int&a,const int&b){return a+b>=mod?a+b-mod:a+b;}
inline int dec(const int&a,const int&b){return a>=b?a-b:a-b+mod;}
inline int mul(const int&a,const int&b){return (ll)a*b%mod;}
inline void Add(int&a,const int&b){a=a+b>=mod?a+b-mod:a+b;}
inline void Dec(int&a,const int&b){a=a>=b?a-b:a-b+mod;}
inline void Mul(int&a,const int&b){a=(ll)a*b%mod;}
const int N=45,M=1e6+5;
int n,l,d[N],f[N][N][N*N<<2],s=0;
int fac[M<<1],ifac[M<<1];
inline void init(int up){
fac[0]=fac[1]=ifac[0]=ifac[1]=1;
for(ri i=2;i<=up;++i)fac[i]=mul(i,fac[i-1]),ifac[i]=mul(ifac[mod-mod/i*i],mod-mod/i);
for(ri i=2;i<=up;++i)Mul(ifac[i],ifac[i-1]);
}
inline int C(int n,int m){return n<m?0:(ll)fac[n]*ifac[m]%mod*ifac[n-m]%mod;}
int main(){
scanf("%d%d",&l,&n);
init(l<<1);
for(ri i=1;i<=n;++i)scanf("%d",&d[i]),--d[i];
sort(d+1,d+n+1),reverse(d+1,d+n+1);
f[0][1][0]=1;
for(ri i=1;i<=n;++i){
s+=d[i];
for(ri j=0;j<=i+1;++j){
for(ri k=0;k<=s*2;++k){
if(j&&k>=2*d[i])Add(f[i][j][k],mul(f[i-1][j-1][k-2*d[i]],j-1));
if(k>=d[i])Add(f[i][j][k],mul(f[i-1][j][k-d[i]],j<<1));
Add(f[i][j][k],mul(f[i-1][j+1][k],j+1));
}
}
}
int ans=0;
for(ri i=0;i<=2*s;++i){
if(!f[n][0][i])continue;
Add(ans,mul(f[n][0][i],C(l-i,n)));
}
cout<<ans;
return 0;
}