题目描述:一个 1 1 到的排列 p[i] p [ i ] ,一个位置是好位置当且仅当 |p[i]−i|=1 | p [ i ] − i | = 1 ,求恰好有 k k 个好位置的排列数
分析:
首先我们可以选出个位置,强制这
k
k
个位置成为good position
其余的数任意排列,这样我们就知道 good position 数大于等于k的排列数了
我们首先考虑如何计算:选出个位置,强制这 i i 个位置成为good position的序列数
先设计一个简单的dp:
表示到第
i
i
位,有个good position
注意:
这个状态只考虑good position,并不能构造合法的序列
也就是说我们只能计算出
j
j
个good position的情况
最后答案是:(剩下的位置随意排列)
考虑第
i
i
位能不能成为good position
那我们就要知道有没有使用过
显然
i+1
i
+
1
是不可能使用过的
那么我们就需要记录一下
i−1
i
−
1
的使用情况:
f[i][j][0/1][0/1]
f
[
i
]
[
j
]
[
0
/
1
]
[
0
/
1
]
表示到第
i
i
位,有个good position,
i
i
用没用过,用没用过
f[i][j][a][b]−>f[i+1]
f
[
i
]
[
j
]
[
a
]
[
b
]
−
>
f
[
i
+
1
]
第 i+1 i + 1 位不选择 −>f[i+1][j][b][0] − > f [ i + 1 ] [ j ] [ b ] [ 0 ]
第 i+1 i + 1 位填 i+2−>f[i+1][j+1][b][1] i + 2 − > f [ i + 1 ] [ j + 1 ] [ b ] [ 1 ]
第 i+1 i + 1 位填 i i ,此时中 a a 必须等于0
然而,这个dp得到的是good position大于等于k的序列
容斥原理?!
实际上我们在计算
f[n][j]
f
[
n
]
[
j
]
的时候,是有大量重复的,那么到底重复了多少次呢?
C(j,k)
C
(
j
,
k
)
次!
ans=
a
n
s
=
f[n][k]∗(n−k)!∗C(k,k)
f
[
n
]
[
k
]
∗
(
n
−
k
)
!
∗
C
(
k
,
k
)
−f[n][k+1]∗(n−(k+1))!∗C(k+1,k)
−
f
[
n
]
[
k
+
1
]
∗
(
n
−
(
k
+
1
)
)
!
∗
C
(
k
+
1
,
k
)
+f[n][k+2]∗(n−(k+2))!∗C(k+2,k)...
+
f
[
n
]
[
k
+
2
]
∗
(
n
−
(
k
+
2
)
)
!
∗
C
(
k
+
2
,
k
)
.
.
.
tip
初始化: f[0][0][1][0]=1 f [ 0 ] [ 0 ] [ 1 ] [ 0 ] = 1
#include<cstdio>
#include<cstring>
#include<iostream>
#define ll long long
using namespace std;
const int N=1002;
const ll p=1e9+7;
ll c[N][N],f[N][N][2][2],jc[N];
int n,k;
void prepare() {
jc[0]=1; jc[1]=1;
for (int i=2;i<=n;i++) jc[i]=(jc[i-1]*(ll)i)%p;
c[0][0]=1;
for (int i=1;i<=n;i++) {
c[i][0]=1;
for (int j=1;j<=i;j++)
c[i][j]=(c[i-1][j]+c[i-1][j-1])%p;
}
}
void dp() {
f[0][0][1][0]=1; //第i位,已有j位GP,i位是否用了,i+1位是否用了
for (int i=0;i<n;i++)
for (int j=0;j<=i;j++)
for (int a=0;a<=1;a++)
for (int b=0;b<=1;b++)
if (f[i][j][a][b]) {
f[i+1][j][b][0]+=f[i][j][a][b]; f[i+1][j][b][0]%=p;
f[i+1][j+1][b][1]+=f[i][j][a][b]; f[i+1][j+1][b][1]%=p;
if (!a)
f[i+1][j+1][b][0]+=f[i][j][a][b]; f[i+1][j+1][b][0]%=p;
}
}
int main() {
scanf("%d%d",&n,&k);
prepare();
dp();
ll ans=0,o=1;
for (int i=k;i<=n;i++) {
ll t=0;
t=(t+f[n][i][0][0]+f[n][i][1][0])%p;
ans=(ans+((o*t%p*c[i][k]%p*jc[n-i])%p+p)%p);
o*=-1;
}
printf("%I64d\n",(ans%p+p)%p);
return 0;
}