持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第28天,点击查看活动详情
[SDOI2016]排列计数
题目描述
求有多少种 到 的排列 ,满足序列恰好有 个位置 ,使得 。
答案对 取模。
输入格式
本题单测试点内有多组数据。
输入的第一行是一个整数 ,代表测试数据的整数。
以下 行,每行描述一组测试数据。
对于每组测试数据,每行输入两个整数,依次代表 和 。
输出格式
共输出 行,对于每组测试数据,输出一行一个整数代表答案。
样例 #1
样例输入 #1
5
1 0
1 1
5 2
100 50
10000 5000
样例输出 #1
0
1
20
578028887
60695423
提示
数据规模与约定
本题共 20 个测试点,各测试点等分,其数据规模如下表。
| 测试点编号 | 测试点编号 | ||||
|---|---|---|---|---|---|
对于全部的测试点,保证 ,,。
解题思路
-
组合数的求法,用阶乘+逆元预处理求组合数,先预处理出数据范围内的阶乘,最后回答。
-
对于错排列的求法,有这么一个递推公式
#include <iostream>
#include <cstdio>
using namespace std;
typedef long long ll;
const ll mod=1000000007;
ll f[1000000+50],inv[1000000+50],d[1000000+50];
ll T;
ll n,m;
ll q_pow(ll a,ll b){
ll ans=1;
while(b){
if(b&1){
ans=ans*a%mod;
}
b>>=1;
a=a*a%mod;
}
return ans%mod;
}
inline void init(){
f[0]=f[1]=d[0]=d[2]=inv[0]=inv[1]=1;f[2]=2;inv[2]=q_pow(f[2],mod-2);
}
inline void prework(){
for(ll i=3;i<=1000000;++i){
f[i]=f[i-1]*i%mod;
inv[i]=q_pow(f[i],mod-2);
d[i]=(i-1)*(d[i-1]+d[i-2])%mod;
}
}
inline ll ask(ll n,ll m){
return (f[n]*inv[m]%mod*inv[n-m]%mod)%mod;
}
inline ll read(){
ll ans;
char c;
while(c=getchar(),c<'0'||c>'9'){
}
ans=c-'0';
while(c=getchar(),c>='0'&&c<='9'){
ans=ans*10+c-'0';
}
return ans;
}
int main(){
init();
prework();
scanf("%lld",&T);
while(T--){
n=read();m=read();
printf("%lld\n",ask(n,m)*d[n-m]%mod);
}
return 0;
}