首先發現剛好很很差算,因此轉成至少,這樣子只須要肯定完一部分數以後剩下隨意補。
而後套一個二項式反演進行容斥就能夠獲得答案了。
考慮怎麼算至少\(m\)個的貢獻,
設\(f[i][j][S]\)表示當前填到了位置\(i\),一個有\(j\)個貢獻,\(i\)和\(i+1\)的使用狀況是\(S\)的方案數,每次枚舉一下這個位置是填\(i+1\)仍是\(i-1\)仍是其餘就能夠進行轉移了。spa
#include<iostream> #include<cstdio> using namespace std; #define MOD 1000000007 #define MAX 1010 void add(int &x,int y){x+=y;if(x>=MOD)x-=MOD;} inline int read() { int x=0;bool t=false;char ch=getchar(); while((ch<'0'||ch>'9')&&ch!='-')ch=getchar(); if(ch=='-')t=true,ch=getchar(); while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar(); return t?-x:x; } int n,m,jc[MAX],a[MAX],ans; int f[MAX][MAX][4],C[MAX][MAX]; int main() { n=read();m=read(); jc[0]=1;for(int i=1;i<=n;++i)jc[i]=1ll*jc[i-1]*i%MOD; for(int i=0;i<=n;++i)C[i][0]=1; for(int i=1;i<=n;++i) for(int j=1;j<=i;++j) C[i][j]=(C[i-1][j]+C[i-1][j-1])%MOD; f[0][0][1]=1; for(int i=1;i<=n;++i) for(int j=0;j<i;++j) for(int S=0;S<4;++S) { if(!f[i-1][j][S])continue; add(f[i][j][S>>1],f[i-1][j][S]); if(!(S&1))add(f[i][j+1][S>>1],f[i-1][j][S]); if(i!=n)add(f[i][j+1][(S>>1)|2],f[i-1][j][S]); } for(int i=0;i<=n;++i) for(int S=0;S<4;++S) add(a[i],1ll*f[n][i][S]*jc[n-i]%MOD); for(int i=m,d=1;i<=n;++i,d=MOD-d)add(ans,1ll*d*C[i][m]%MOD*a[i]%MOD); printf("%d\n",ans); return 0; }