[SDOI2016]排列计数

顺着题面来推,长度为n的数列中有m个位置不变,相当与求从n里选m个的组合数。剩余可变的位置相当于求错排。

数据范围n≤1000000,不能递推求组合数,可以直接公式计算,但同时要求逆元来支持取模运算。

因此需要递推求最大范围内的组合数和错排,两者都是线性的,所以时间复杂度都是O(1000000),每读入一组数据直接公式输出就行。

#include<cstdio>
using namespace std;
#define maxn 1000005
const long long mod=1000000007;
long long T;
long long js[maxn+5],F[maxn+5];
inline void pre_fir()
{
    js[0]=1;
    for(int i=1;i<=maxn;i++) js[i]=js[i-1]*i,js[i]%=mod;
    F[0]=1,F[1]=0,F[2]=1;
    for(int i=3;i<=maxn;i++)
    {
        F[i]=(i-1)*(F[i-1]+F[i-2]);
        F[i]%=mod;
    }
}
inline long long qpow(long long x,long long y)
{
    long long ans=1;
    while(y!=0)
    {
        if(y&1) ans=ans%mod*x%mod;
        x=x%mod*x%mod;
        y>>=1;
    }
    return ans%mod;
}
inline long long inv(long long x)
{
    return qpow(x,(long long)(mod-2));
}
inline long long C(long long x,long long y)
{
    return js[x]%mod*inv(js[y])%mod*inv(js[x-y])%mod;
}
int main()
{
    scanf("%lld",&T);
    pre_fir();
    while(T--)
    {
        long long n,m;
        scanf("%lld%lld",&n,&m);
        if(m>n) printf("0
");
        else printf("%lld
",C(n,m)%mod*F[n-m]%mod);
    }
    return 0;
}
原文地址:https://www.cnblogs.com/charlesss/p/10980892.html