简化版题意:
1~n n个数字,问满足m个ai=i的排列个数
答案对1e9+7取模
这题就是道裸题,不知道为啥还能是蓝的
前置技能一:快速幂(太简单了不讲了 这周和矩阵的知识点一起写)
前置技能二:错排公式(顾名思义错排就是ai!=i的排列个数,高中应该都学过)
下面是推理过程:我们设f[n]代表n个数的错排结果,我们考虑从n-1个变到n的过程:
对于来的第n个数,我们把它放到第i个位置,那么i就有两种情况:
- i放到n,此时n-2个数有f[n-2]种情况
- i不放到n,此时我们可以把i看成n,那么剩下这n-1个数有f[n-1] 种情况
- i有n-1种选法,所以f[n]=(n-1)*(f[n-1]+f[n-2])
前置技能三:乘法逆元求解:这题俺用的费马小定理,其他两种这周也会写(咕咕咕警告)
这些你都会了
就完事了
#include <cstdio>
#include <iostream>
#include <algorithm>
#define int long long
using namespace std;
const int modd=1e9+7;
int t,n,m;
int f[1000005];
int d[1000005];
int inv[1000055];
inline int read(){
int x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
int ksm(int a,int b){
int ans=1;
while(b>0){
if(b&1) ans=ans*a%modd;
a=a*a%modd;
b>>=1;
}
return ans%modd;
}
inline int C(int x,int y){
return (d[x]*inv[y]%modd*inv[x-y]%modd)%modd;
}
main(){
t=read();
d[0]=d[1]=f[0]=f[2]=inv[0]=inv[1]=1;d[2]=2;inv[2]=ksm(d[2],modd-2);
for(int i=3;i<=1000000;i++){
f[i]=(i-1)*(f[i-1]+f[i-2])%modd;//错排公式
d[i]=d[i-1]*i%modd;
inv[i]=ksm(d[i],modd-2)%modd;
}
while(t--){
n=read();m=read();
int ans=(C(n,m)%modd*f[n-m])%modd;
ans=ans%modd;
ans=ans+modd;
ans=ans%modd;
printf("%lld\n",ans);
}
return 0;
}