【题意】
求有多少种长度为 n 的序列 A,满足:
①1 ~ n 这 n 个数在序列中各出现了一次;
②若第 i 个数 A[i] 的值为 i,则称 i 是稳定的。序列恰好有 m 个数是稳定;
满足条件的序列可能很多,序列数对 10^9+7 取模。
数据范围: T=500000,n≤1000000,m≤1000000
【分析】
对于稳定的数,有
Cmn=n!m!(n−m)!
种选择,很明显预处理出
1!
到
n!
的值,还有
1!
到
n!
的所有拟元即可。
对于不稳定的数,有
n−m
个,这个问题等价于一个左右各有
n−m
个点的二分图,现在求有多少种完备匹配,满足横向不连边。
由于最多
n
个,很明显并不需要得到封闭形式,只用得到边界和递推形式就可以了。
设
边界:
递推:假设当前已经求出了
a0,a1,...,an−1
,现在要求
an
因为是完备匹配,所以
n
一定在一个连边组成的联通块之中。
枚举联通块的大小为k,则这时有
所以
an=∑nk=2Pk−1n−1an−k=∑nk=2(n−1)!(n−k)!∗an−k
更换求和指标,
an=(n−1)!∑n−2k=0akk!=[(n−1)(n−2)!]∗[∑n−3k=0akk!+an−2(n−2)!]=(n−1)(n−2)!∑n−3k=0akk!+(n−1)∗(n−2)!∗an−2(n−2)!=(n−1)an−1+(n−1)an−2=(n−1)(an−1+an−2)
所以用这个递推式求出所有的
a
<script type="math/tex" id="MathJax-Element-66">a</script>。
【代码】
#include <cstdio>
#include <cctype>
typedef long long LL;
const int N=1000001;
const int L=1000000007;
int f[N];
int g[N],inv[N];
int cas;
int n,m;
int res;
inline int read(void)
{
int x=0; char c=getchar();
for (;!isdigit(c);c=getchar());
for (;isdigit(c);c=getchar()) x=x*10+c-'0';
return x;
}
int mi(int i,int j)
{
if (!j) return 1;
int mtp=mi(i,j>>1);
mtp=(LL)mtp*mtp%L;
if (j&1) mtp=(LL)mtp*i%L;
return mtp;
}
int main(void)
{
// freopen("a.in","r",stdin);
// freopen("a.out","w",stdout);
f[0]=1,f[1]=0;
for (int i=2;i<N;i++)
f[i]=((LL)f[i-1]+f[i-2])*(i-1)%L;
g[0]=1;
for (int i=1;i<N;i++)
g[i]=(LL)g[i-1]*i%L;
for (int i=0;i<N;i++)
inv[i]=mi(g[i],L-2);
cas=read();
for (int cc=1;cc<=cas;cc++)
{
n=read(),m=read();
if (n>=m)
res=(LL)g[n]*inv[m]%L*inv[n-m]%L*f[n-m]%L;
else res=0;
//res=C(n,m)*a[n-m]= n! / m! / (n-m)! * a[n-m]
printf("%d\n",res);
}
return 0;
}