4517: [Sdoi2016]排列计数
Time Limit: 60 Sec Memory Limit: 128 MBSubmit: 496 Solved: 298
[ Submit][ Status][ Discuss]
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
1 0
1 1
5 2
100 50
10000 5000
Sample Output
0
1
20
578028887
60695423
1
20
578028887
60695423
HINT
Source
题解:c(n,m)*d(n-m) c表示组合,d表示错排
c(n,m)=n!/m!(n-m)! d(n)=(n-1)(d(n-1)+d(n-2))
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define p 1000000007
#define N 1000000
#define ll long long
using namespace std;
ll d[N+3],jc[N+3];
ll n,m,t;
ll quickpow(ll num,ll x)
{
ll base=num%p; ll ans=1;
while (x)
{
if (x&1)
ans=ans*base%p;
x>>=1;
base=base*base%p;
}
return ans%p;
}
ll solve(ll n,ll m)
{
ll a=quickpow(jc[m]*jc[n-m],p-2);
return jc[n]*a%p;
}
void calc()
{
jc[1]=1;
for (ll i=2;i<=N;i++)
jc[i]=jc[i-1]*i%p;
}
int main()
{
scanf("%lld",&t);
d[0]=1; d[1]=0; d[2]=1;
for (ll i=3;i<=N;i++)
d[i]=(i-1)*(d[i-1]+d[i-2])%p;
calc();
for (int T=1;T<=t;T++)
{
scanf("%lld%lld",&n,&m);
if (m==0)
{
printf("%lld\n",d[n]);
continue;
}
if (n==m)
{
printf("1\n");
continue;
}
ll ans=solve(n,m)*d[n-m]%p;
printf("%lld\n",ans%p);
}
}