嘘~ 正在从服务器偷取页面 . . .

洛谷P4071 [SDOI2016]排列计数 题解


洛谷P4071 [SDOI2016]排列计数 题解

题目链接:P4071 [SDOI2016]排列计数

题意

求有多少种 $1$ 到 $n$ 的排列 $a$,满足序列恰好有 $m$ 个位置 $i$,使得 $a_i = i$。

答案对 $10^9 + 7$ 取模。

输入格式

输入的第一行是一个整数 $T$,代表测试数据的整数。

以下 $T$ 行,每行描述一组测试数据。

对于每组测试数据,每行输入两个整数,依次代表 $n$ 和 $m$。

输出格式

共输出 $T$ 行,对于每组测试数据,输出一行一个整数代表答案。

数据范围

$1 \leq T \leq 5 \times 10^5$,$1 \leq n \leq 10^6$,$0 \leq m \leq 10^6$。

答案:

其中 $D(n)$ 为 $n$ 个数的错排公式,有

注意特判 $n<m$ 和 $n=m$ 的情况。

时间复杂度 $\mathcal{O}(N+Q)$

代码:

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
void up(int &x,int y) { x < y ? x = y : 0; }
void down(int &x,int y) { x > y ? x = y : 0; }
#define N ((int)(1e6+15))
const int p = 1e9 + 7;

int d[N],fac[N],invf[N];
int qpow(int a, int b)
{
    int ans = 1, base = a % p;
    for(; b; b >>= 1)
    {
        if(b & 1) ans = ans * base % p;
        base = base * base % p;
    }
    return ans;
}
void init()
{
    fac[0] = 1;
    for(int i=1; i<=N-5; i++) fac[i] = fac[i-1] * i % p;
    invf[N-5] = qpow(fac[N-5], p-2);
    for(int i=N-5; i; i--) invf[i-1] = invf[i] * i % p; 
    d[1] = 0; d[2] = 1;
    for(int i=3; i<=N-5; i++) d[i] = (i-1) * ((d[i-1] + d[i-2])%p) % p;
}
int C(int n,int m)
{ return fac[n] * invf[m] % p * invf[n-m] % p; }
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    // freopen("check.in","r",stdin);
    // freopen("check.out","w",stdout);
    int Q,n,m; init();
    for(cin >> Q; Q--; )
    {
        cin >> n >> m;
        if(n < m) cout << "0\n";
        else if(n == m) cout << "1\n";
        else cout << C(n,m) * d[n-m] % p << '\n';
    }
    return 0;
}

文章作者: q779
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-ND 4.0 许可协议。转载请注明来源 q779 !
评论
  目录