题意:对于某个排列p1,p2,...pn,位置i为good position当且仅当| pi - i | = 1,给你一个N,表示排列的元素个数,求满足恰好有K个good position的排列的个数,对结果Mod 1000000007。
思路:DP。首先很容易就可以想到如下的状态dp,用dp[i][j] 表示前i个位置中正好有j个位置是good position的种数。那么对于第i个位置,它就可以分成两大种情况:
a、第i个位置不是good ; 这时候我们可以很容易地实现状态转移:dp[i][j] = dp[i-1][j] 。
b、第i个位置是good。 这种情况又可以分成两种小情况:
b1.i的取值(即pi的值)是i-1; 但是这里有会带来另外一个问题,那就是i-1是否可用的问题,换句话说就是i-2位置是否已经将i-1这个数已经用掉了,所以我们可以再加入一维状态s来表示i的最后三位的状态。
b2.i的取值是i+1 ,此时可以不受前面限制地进行转移。
最后我们可以由dp求出整个排列中只有i个元素是good的值,记为sum[i],sum[i] *= fac[N-i],那么sum[i]就是至少有i个元素是good的总数,这里还特别需要注意的一点就是sum[i] = a[i] + C[i+1][i]*a[i+1] + C[i+2][i]*a[i+2]....,所以我们最后可以反解出a[K]。
代码:
#include <stdio.h>
#include <string.h>
typedef long long LL ;
const int NN = 1010 ;
const LL Mod = 1000000007 ;
LL N , K ;
LL dp[NN][NN][2][2][2] ;
LL sum[NN] ;
LL fac[NN] ;
LL a[ NN ] ;
LL C[NN][NN] ;
void DP(){
memset( dp, 0 ,sizeof(dp) ) ;
dp[1][0][0][0][0] = 1 ;
if( N > 1 )
dp[1][1][0][0][1] = 1 ;
for(int i=1;i<N;i++){
for(int j=0;j<=i;j++){
for(int a=0;a<2;a++){
for(int b=0;b<2;b++){
for(int c=0;c<2;c++){
if( dp[i][j][a][b][c] == 0 ) continue ;
int tmp = dp[i][j][a][b][c] ;
dp[i+1][j][b][c][0] = ( dp[i+1][j][b][c][0] + tmp ) % Mod ;
if( b == 0 ) dp[i+1][j+1][b][c][0] = ( dp[i+1][j+1][b][c][0] + tmp ) % Mod ;
if( i+2<=N ) dp[i+1][j+1][b][c][1] = ( dp[i+1][j+1][b][c][1] + tmp ) % Mod ;
}
}
}
}
}
for(int i=0;i<=N;i++){
sum[i] = 0 ;
sum[i] = dp[N][i][0][0][0] + dp[N][i][0][0][1] +
dp[N][i][0][1][0] + dp[N][i][0][1][1] +
dp[N][i][1][0][0] + dp[N][i][1][0][1] +
dp[N][i][1][1][0] + dp[N][i][1][1][1] ;
sum[i] %= Mod ;
sum[i] = sum[i] * fac[N-i] % Mod ;
}
}
int main(){
fac[1] = fac[0] = 1 ;
for(LL i=2;i<=1000;i++) fac[i] = fac[i-1] * i % Mod ;
C[0][0] = C[1][0] = C[1][1] = 1 ;
for(int i=2;i<=1000;i++){
for(int j=0;j<=i;j++){
C[i][j] = C[i-1][j-1] ;
if( i-1>=j ) C[i][j] = (C[i][j] + C[i-1][j]) % Mod ;
}
}
while( scanf("%d %d",&N,&K) == 2 ){
DP() ;
a[N] = sum[N] ;
for(int i=N-1;i>=K;i--){
LL tmp = sum[i] ;
for(int j=i+1;j<=N;j++){
LL mid = C[j][i] * a[j] ;
mid %= Mod ;
tmp -= mid ;
if( tmp < 0 ) tmp += Mod ;
}
a[i] = tmp ;
}
printf("%d\n",(int)a[K] );
}
return 0 ;
}