洛谷 P4071 [SDOI2016]排列计数

洛谷

这是一道组合数学题。

对于一个长为n的序列,首先我们要选m个使之稳定\(C^{m}_{n}\)

且要保证剩下的序列不稳定,即错排\(D_{n-m}\)

所以答案就是:$$ANS=C^{m}{n}+D$$

再看看数据范围:n最大\(10^6\),错排好办,直接递推:

\[D[i]=(i-1)*(D[i-1]+D[i-2]) \]

D[0]=1,D[1]=0。

而组合数部分有点麻烦。$$C[i][j]=C[i-1][j]+C[i-1][j-1]$$

用上面这个公式可以做1000的点,\(n^2\)递推。

至于满分,我们可以用普通的组合数公式:$$C(n,m)=n!/[(n-m)!m!]$$

我们预处理ni[]表示x的阶乘。

接下来很好做了,除法取模涉及逆元,因为模数是质数,直接费马\(t^{\texttt{mod}-2}\)

所以代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

const int mo=1e9+7;
int n,m;
ll D[1000001]={1,0,1},ni[1000001]={1};

void pre()
{
    for (int i=3;i<=1000000;++i)
        D[i]=(i-1)*(D[i-1]+D[i-2])%mo;
    for (int i=1;i<=1000000;++i)
        ni[i]=ni[i-1]*i%mo;
}

ll qpow(ll x)
{
    ll p=mo-2,d=1;
    while (p) {
        if (p&1) d=d*x%mo;
        x=x*x%mo;p>>=1;
    }
    return d;
}

int main()
{
    pre();
    int T;cin>>T;
    while (T--) {
        scanf("%d%d",&n,&m);
        ll C=ni[n]*qpow(ni[m]*ni[n-m]%mo)%mo;
        printf("%lld\n",C*D[n-m]%mo);
    }
    return 0;
}
posted @ 2018-08-21 18:57  fuyan0101  阅读(147)  评论(0)    收藏  举报