bzoj 4517: [Sdoi2016]排列计数

4517: [Sdoi2016]排列计数

Time Limit: 60 Sec   Memory Limit: 128 MB
Submit: 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

Sample Output

0
1
20
578028887
60695423

HINT

Source

鸣谢Menci上传

[ Submit][ Status][ Discuss] 

题解: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);
     }
}



你可能感兴趣的:(bzoj 4517: [Sdoi2016]排列计数)