4498: 魔法的碰撞
我们先考虑全部紧凑的情况,也就是没有多余的空格的情况(将 D i D_i Di–,先不考虑魔法师占的空间)。
这里用了一个很巧妙的方法,多加一维,表示预留的空位。
加入A会有三种情况(E表示空位):A,AE或EA,EAE。
对于第一种,表示A两边都有魔法师。
对于第二种,表示A的一边有,一边没有。
对于第三种,表示A的两边都是空的。
如果我们按照从大到小的顺序来,就会发现只需要考虑当前情况就可以了。
比如说是EAE的情况,我们就可以直接加上A的距离了。
此时可以设 F [ i ] [ j ] [ k ] F[i][j][k] F[i][j][k]表示前i个魔法师,还有j个空位,用了k距离的方案数。
最后将那些多余的空格放入,用组合数(隔板法)算一下就可以了。
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int MOD=1000000007;
int m,n,Ans,Sum,fac[1000005],inv[1000005],a[45],f[2][45][10005];
void MO(int &x){if(x>=MOD) x-=MOD;}
int qsm(int x,int b){
int Mul=1;
for(;b;b>>=1,x=1ll*x*x%MOD) if(b&1) Mul=1ll*Mul*x%MOD;
return Mul;
}
int C(int x,int y){if(x<y) return 0;return 1ll*fac[x]*inv[y]%MOD*inv[x-y]%MOD;}
bool cmp(int x,int y){return x>y;}
int main(){
scanf("%d%d",&m,&n);fac[0]=1;
for(int i=1;i<=m;i++) fac[i]=1ll*fac[i-1]*i%MOD;
inv[m]=qsm(fac[m],MOD-2);
for(int i=m-1;i>=0;i--) inv[i]=1ll*inv[i+1]*(i+1)%MOD;
for(int i=1;i<=n;i++) scanf("%d",&a[i]),--a[i];
sort(a+1,a+1+n,cmp);f[0][1][0]=1;
for(int i=1,lst=0,now=1;i<=n;i++,lst^=1,now^=1){
Sum+=a[i];
for(int j=0;j<=i+1;j++)
for(int k=0;k<=(Sum<<1);k++){
f[now][j][k]=1ll*(j+1)*f[lst][j+1][k]%MOD;
if(k>=a[i]) MO(f[now][j][k]+=2ll*j*f[lst][j][k-a[i]]%MOD);
if(j&&k>=(a[i]<<1)) MO(f[now][j][k]+=1ll*(j-1)*f[lst][j-1][k-(a[i]<<1)]%MOD);
}
}
for(int i=0;i<=min(Sum<<1,m-n);i++) MO(Ans+=1ll*f[n&1][0][i]*C(m-i,n)%MOD);
printf("%d\n",Ans);
return 0;
}