SDOI 2016 排列计数
设满足题目条件且长度为n恰好有m个数稳定的序列数为
a
n
s
(
n
,
m
)
ans(n,m)
ans(n,m)
那么易得题目答案ANS可表示为:
A
N
S
=
a
n
s
(
n
,
m
)
=
C
(
n
,
m
)
∗
a
n
s
(
n
−
m
,
0
)
ANS=ans(n,m)=C(n,m)*ans(n-m,0)
ANS=ans(n,m)=C(n,m)∗ans(n−m,0)
因为题目规定
n
,
m
≤
1
e
6
n,m\le1e6
n,m≤1e6,所以C(n,m)可以通过预处理阶乘+乘法逆元快速求得。
至于ans(n-m,0),又被称作为全错位排位数。
即n个相异的元素排成一排且
a
i
≠
i
a_i\ne i
ai=i的排列个数。
设长度为n的全错位排列数为Fn,有如下递推公式:
F
n
=
(
n
−
1
)
(
F
n
−
1
+
F
n
−
2
)
,
n
≥
3
F_n=(n-1)(F_{n-1}+F_{n-2}),n\ge3
Fn=(n−1)(Fn−1+Fn−2),n≥3,其中
F
1
=
0
,
F
2
=
1
F_1=0,F_2=1
F1=0,F2=1。另外对于长度为0的序列,规定其全错位排列数为1。
证明:依据递推的思想,显然
F
1
=
0
,
F
2
=
1
F_1=0,F_2=1
F1=0,F2=1,对于
n
≥
3
n\ge3
n≥3,不妨设第n个数排在了第
k
(
k
≠
n
)
k(k\ne n)
k(k=n)位。
1、若第k个数排在了第n位,那么对于n和k两个位置满足
a
n
≠
n
,
a
k
=
n
≠
k
a_n\ne n,a_k=n\ne k
an=n,ak=n=k,那么只剩下了n-2个数,显然剩下n-2个数可构成的全错位排列数为
F
n
−
2
F_{n-2}
Fn−2
2、若第k个数没有排在第n位,则规定了
a
n
≠
k
a_n\ne k
an=k,那么可以将第n位假想为第k位,那么问题就转换成了n-1个数的全错位排列问题,解为
F
n
−
1
F_{n-1}
Fn−1。
由于
1
≤
k
<
n
1\le k< n
1≤k<n,所以
F
n
=
(
n
−
1
)
(
F
n
−
1
+
F
n
−
2
)
F_n=(n-1)(F_{n-1}+F_{n-2})
Fn=(n−1)(Fn−1+Fn−2)
接下来利用容斥原理来推出 F n F_n Fn的通项公式:
设n个数组成的全排列集合为I,则
∣
I
∣
=
n
!
|I|=n!
∣I∣=n!。设满足
a
i
=
i
a_i=i
ai=i的排列的集合为
A
i
(
1
≤
i
≤
n
)
A_i(1\le i \le n)
Ai(1≤i≤n)。有
∣
A
i
∣
=
(
n
−
1
)
!
|A_i|=(n-1)!
∣Ai∣=(n−1)!,
∣
A
i
∩
A
j
∣
=
(
n
−
2
)
!
(
1
≤
i
<
j
≤
n
)
|A_i\cap A_j|=(n-2)!(1\le i<j\le n)
∣Ai∩Aj∣=(n−2)!(1≤i<j≤n),
∣
A
i
∩
A
i
+
1
∩
.
.
∩
A
i
+
k
∣
=
(
n
−
k
−
1
)
!
|A_i\cap A_{i+1}\cap..\cap A_{i+k}|=(n-k-1)!
∣Ai∩Ai+1∩..∩Ai+k∣=(n−k−1)!
有
F
n
=
∣
I
∣
−
∣
A
1
∪
A
2
∪
.
.
.
∪
A
n
∣
=
∣
I
∣
−
(
C
n
1
∣
A
1
∣
−
C
n
2
∣
A
1
∩
A
2
∣
+
C
n
3
∣
A
1
∩
A
2
∩
A
3
∣
+
.
.
+
(
−
1
)
n
−
1
∣
A
1
∩
A
2
∩
.
.
∩
A
n
∣
)
=
n
!
−
C
n
1
(
n
−
1
)
!
+
C
n
2
(
n
−
2
)
!
+
.
.
.
+
(
−
1
)
i
C
n
i
(
n
−
i
)
!
+
.
.
+
(
−
1
)
n
C
n
n
0
!
=
n
!
∑
i
=
0
n
(
−
1
)
i
1
i
!
\begin{aligned}F_n&=|I|-|A_1\cup A_2\cup...\cup A_n|\\ &=|I|-(C_n^1|A_1|-C_n^2|A_1\cap A_2|+C_n^3|A_1\cap A_2\cap A_3|+..+(-1)^{n-1}|A_1\cap A_2\cap ..\cap A_n|)\\&=n!-C_n^1(n-1)!+C_n^2(n-2)!+...+(-1)^{i}C_n^i(n-i)!+..+(-1)^{n}C_n^n0!\\&=n!\sum_{i=0}^{n}(-1)^{i}\frac{1}{i!}\end{aligned}
Fn=∣I∣−∣A1∪A2∪...∪An∣=∣I∣−(Cn1∣A1∣−Cn2∣A1∩A2∣+Cn3∣A1∩A2∩A3∣+..+(−1)n−1∣A1∩A2∩..∩An∣)=n!−Cn1(n−1)!+Cn2(n−2)!+...+(−1)iCni(n−i)!+..+(−1)nCnn0!=n!i=0∑n(−1)ii!1
从而也可以验证递推公式:
F
n
+
1
=
n
⋅
(
F
n
+
F
n
−
1
)
=
n
⋅
(
n
!
∑
i
=
0
n
(
−
1
)
i
1
i
!
+
(
n
−
1
)
!
∑
i
=
0
n
−
1
(
−
1
)
i
1
i
!
)
=
n
⋅
(
n
!
∑
i
=
0
n
−
1
(
−
1
)
i
1
i
!
+
(
−
1
)
n
+
(
n
−
1
)
!
∑
i
=
0
n
−
1
(
−
1
)
i
1
i
!
=
(
n
+
1
)
!
∑
i
=
0
n
−
1
(
−
1
)
i
1
i
!
+
(
−
1
)
n
⋅
n
=
(
n
+
1
)
!
⋅
(
∑
i
=
0
n
−
1
(
−
1
)
i
1
i
!
+
(
−
1
)
n
⋅
n
(
n
+
1
)
!
)
=
(
n
+
1
)
!
⋅
(
∑
i
=
0
n
−
1
(
−
1
)
i
1
i
!
+
(
−
1
)
n
⋅
n
+
1
−
1
(
n
+
1
)
!
)
=
(
n
+
1
)
!
⋅
(
∑
i
=
0
n
−
1
(
−
1
)
i
1
i
!
+
(
−
1
)
n
⋅
1
n
!
+
(
−
1
)
n
+
1
1
(
n
+
1
)
!
)
=
(
n
+
1
)
!
⋅
∑
i
=
0
n
+
1
(
−
1
)
i
1
i
!
=
F
n
+
1
\begin{aligned}F_{n+1}&=n\cdot (F_n+F_{n-1})\\&=n\cdot (n!\sum_{i=0}^{n}(-1)^{i}\frac{1}{i!}+(n-1)!\sum_{i=0}^{n-1}(-1)^{i}\frac{1}{i!})\\&=n\cdot(n!\sum_{i=0}^{n-1}(-1)^{i}\frac{1}{i!}+(-1)^n+(n-1)!\sum_{i=0}^{n-1}(-1)^{i}\frac{1}{i!}\\&=(n+1)!\sum_{i=0}^{n-1}(-1)^{i}\frac{1}{i!}+(-1)^n\cdot n\\&=(n+1)!\cdot(\sum_{i=0}^{n-1}(-1)^{i}\frac{1}{i!}+(-1)^n\cdot\frac{n}{(n+1)!})\\&=(n+1)!\cdot(\sum_{i=0}^{n-1}(-1)^{i}\frac{1}{i!}+(-1)^n\cdot\frac{n+1-1}{(n+1)!})\\&=(n+1)!\cdot(\sum_{i=0}^{n-1}(-1)^{i}\frac{1}{i!}+(-1)^n\cdot\frac{1}{n!}+(-1)^{n+1}\frac{1}{(n+1)!})\\&=(n+1)!\cdot\sum_{i=0}^{n+1}(-1)^i\frac{1}{i!}\\&=F_{n+1}\end{aligned}
Fn+1=n⋅(Fn+Fn−1)=n⋅(n!i=0∑n(−1)ii!1+(n−1)!i=0∑n−1(−1)ii!1)=n⋅(n!i=0∑n−1(−1)ii!1+(−1)n+(n−1)!i=0∑n−1(−1)ii!1=(n+1)!i=0∑n−1(−1)ii!1+(−1)n⋅n=(n+1)!⋅(i=0∑n−1(−1)ii!1+(−1)n⋅(n+1)!n)=(n+1)!⋅(i=0∑n−1(−1)ii!1+(−1)n⋅(n+1)!n+1−1)=(n+1)!⋅(i=0∑n−1(−1)ii!1+(−1)n⋅n!1+(−1)n+1(n+1)!1)=(n+1)!⋅i=0∑n+1(−1)ii!1=Fn+1
代码如下:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int max_n=1e6+5;
const int mod=1e9+7;
ll factorial[max_n];
ll f[max_n];
ll rev[max_n];
void init(void)
{
memset(rev,-1,sizeof(rev));
factorial[0]=1;
f[0]=1;
f[1]=0;f[2]=1;
for(ll i=1;i<max_n;i++)
factorial[i]=factorial[i-1]*i%mod;
for(ll i=3;i<max_n;i++)
f[i]=((i-1)*(f[i-1]+f[i-2]))%mod;
}
ll qpow(ll index,ll n,ll p)
{
if(rev[index]!=-1)return rev[index];
ll a=factorial[index];
ll ans=1;
while(n)
{
if(n&1)ans=ans*a%p;
a=a*a%p;
n>>=1;
}
return rev[index]=ans;
}
ll C(ll n,ll m)
{
if(m>n)return 0;
return factorial[n]*qpow(m,mod-2,mod)%mod*qpow(n-m,mod-2,mod)%mod;
}
int main(void)
{
int t;
init();
scanf("%d",&t);
ll n,m;
while(t--)
{
scanf("%lld%lld",&n,&m);
printf("%lld\n",C(n,m)*f[n-m]%mod);
}
}
//5
//1 0
//1 1
//5 2
//100 50
//10000 5000
博客介绍了SDOI 2016竞赛中的排列计数问题,涉及全错位排列数的概念及其递推公式。通过容斥原理给出通项公式,并提供代码实现,用于快速计算特定条件下的排列数。
2970

被折叠的 条评论
为什么被折叠?



