Description
求有多少种长度为 n 的序列 A,满足以下条件:
1 ~ n 这 n 个数在序列中各出现了一次
若第 i 个数 A[i] 的值为 i,则称 i 是稳定的。序列恰好有 m 个数是稳定的
满足条件的序列可能很多,序列数对 10^9+7 取模。
Input
第一行一个数 T,表示有 T 组数据。
接下来 T 行,每行两个整数 n、m。
T=500000,n≤1000000,m≤1000000
Output
输出 T 行,每行一个数,表示求出的序列数
Sample Input
5
1 0
1 1
5 2
100 50
10000 5000
Sample Output
0
1
20
578028887
60695423
Solution
显然,选m个稳定的数的方案数为Cmn,剩余n−m个数要错排
显
然
,
选
m
个
稳
定
的
数
的
方
案
数
为
C
n
m
,
剩
余
n
−
m
个
数
要
错
排
设g(n)表示n个数错排的答案,考虑把n放在位置k上,这样有(n−1)种方案
设
g
(
n
)
表
示
n
个
数
错
排
的
答
案
,
考
虑
把
n
放
在
位
置
k
上
,
这
样
有
(
n
−
1
)
种
方
案
1.把k放在n位置上,就是剩下的(n−2)个数错排
1.
把
k
放
在
n
位
置
上
,
就
是
剩
下
的
(
n
−
2
)
个
数
错
排
2.把k放在不为n的位置上,即剩下(n−1)个数错排
2.
把
k
放
在
不
为
n
的
位
置
上
,
即
剩
下
(
n
−
1
)
个
数
错
排
所以,g(n)=(n−1)∗(g(n−1)+g(n−2))
所
以
,
g
(
n
)
=
(
n
−
1
)
∗
(
g
(
n
−
1
)
+
g
(
n
−
2
)
)
Ans=Cmn∗g(n−m)
A
n
s
=
C
n
m
∗
g
(
n
−
m
)
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
#define P 1000000007
#define LL long long
#define N 1000100
LL f[N],s[N],g[N],ans;
int t,n,m;
LL ksm(LL x,int k)
{
if (k==0) return 1;
if (k==1) return x;
LL t=ksm(x,k/2);
if (k%2==0) return t*t%P;
else return t*t%P*x%P;
}
void pre()
{
f[0]=1;
for (int i=1;i<=1000000;i++)
f[i]=f[i-1]*i%P;
s[0]=1;
for (int i=1;i<=1000000;i++)
s[i]=ksm(f[i],P-2);
g[0]=1; g[1]=0; g[2]=1;
for (int i=3;i<=1000000;i++)
g[i]=(i-1)*((g[i-1]+g[i-2])%P)%P;
}
int main()
{
pre();
scanf("%d",&t);
for (int ii=1;ii<=t;ii++)
{
scanf("%d%d",&n,&m);
ans=0;
ans=f[n]*s[n-m]%P*s[m]%P;
ans=ans*g[n-m]%P;
printf("%lld\n",ans);
}
return 0;
}