DestinHistoire

 

BZOJ-4517 [Sdoi2016]排列计数(错排公式)

题目描述

  求有多少种长度为 \(n\) 的序列 \(A\),满足以下条件:

  $ 1$ ~ \(n\)\(n\) 个数在序列中各出现了一次。

  若第 \(i\) 个数 \(A[i]\) 的值为 \(i\),则称 \(i\) 是稳定的。序列恰好有 \(m\) 个数是稳定的。

  满足条件的序列可能很多,序列数对 \(10^9+7\) 取模。

  数据范围:\(T=5\times 10^5,n\leq 10^6,m\leq 10^6\)

分析

  首先从 \(n\) 个数中选择 \(m\) 个数字,其余数字错排,则方案数为 \(\dbinom{n}{m}\times dp[n-m]\)

  其中 \(dp[0]=1,dp[1]=0,dp[2]=1,dp[i]=(dp[i-1]+dp[i-2])\times (i-1)\)

代码

#include<iostream>
#include<cstdio>
using namespace std;
const int mod=1e9+7;
const int N=1e6+10;
long long quick_pow(long long a,long long b)
{
    long long ans=1;
    while(b)
    {
        if(b&1)
            ans=ans*a%mod;
        a=a*a%mod;
        b>>=1;
    }
    return ans;
}
long long dp[N+10],fac[N+10],inv[N+10];
void init()
{
    fac[0]=inv[0]=1;
    for(int i=1;i<=N;i++)
        fac[i]=fac[i-1]*i%mod;
    inv[N]=quick_pow(fac[N],mod-2);
    for(int i=N-1;i>=1;i--)
        inv[i]=inv[i+1]*(i+1)%mod;
    dp[0]=1;dp[1]=0;dp[2]=1;
    for(int i=3;i<=N;i++)
        dp[i]=(dp[i-1]+dp[i-2])%mod*(i-1)%mod;
}
long long C(long long n,long long m)
{
    if(m>n)
        return 0;
    return fac[n]*inv[m]%mod*inv[n-m]%mod;
}
int main()
{
    init();
    int T;
    cin>>T;
    while(T--)
    {
        long long n,m;
        scanf("%lld %lld",&n,&m);
        printf("%lld\n",dp[n-m]*C(n,m)%mod);
    }
    return 0;
}

posted on 2020-11-09 17:31  DestinHistoire  阅读(93)  评论(0)    收藏  举报

导航