洛谷题单指南-组合数学与计数-P4071 [SDOI2016] 排列计数

原题链接:https://www.luogu.com.cn/problem/P4071

题意解读:1~n的数字排列,有m个不错位,求有多少种排列。

解题思路:

错排问题:1~n的排列中,数字与位置编号不相同称之为错位。

错排的方案数可以用递推计算,设f(n)表示1~n的错排数,

当考虑到数字n,先将n放在第n号位,前面的n-1个数已形成的排列有两种可能:

1、前n-1个数已经是错排,则将n与前n-1任意一个数交换即可得到n个数的错排,方案一共是(n-1) * f(n-1)

2、前n-1数中有一个数没有错排,则将n与这个数交换即可得到n个数的错排,由于前n-1个数有一个没有错排,方案数一共是(n-1)*f(n-2),

根据加法原理,f(n) = (n-1)(f(n-1) + f(n-2))

回到此题,n个数要有m个没有错排,先选出m个数c(n,m),再将n-m个数错排f(n-m),所以一共是c(n,m) * f(n-m)

100分代码:

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

typedef long long LL;
const int N = 1e6 + 5, MOD = 1e9 + 7;
LL fac[N], inv[N], f[N];
LL T, n, m;

LL ksm(LL a, LL b, LL mod)
{
    LL res = 1;
    while(b)
    {
        if(b & 1) res = res * a % mod;
        a = a * a % mod;
        b >>= 1;
    }
    return res;
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    fac[0] = inv[0] = 1;
    for(LL i = 1; i <= N; i++)
    {
        fac[i] = fac[i - 1] * i % MOD; //阶乘的模
        inv[i] = inv[i - 1] * ksm(i, MOD - 2, MOD) % MOD; //阶乘的逆元的模
    }

    f[0] = 1, f[1] = 0;
    for(LL i = 2; i <= N; i++) f[i] = (i - 1) * (f[i - 1] + f[i - 2]) % MOD; //错排公式
    
    cin >> T;
    while(T--)
    {
        cin >> n >> m;
        LL ans = fac[n] * inv[n - m] % MOD * inv[m] % MOD * f[n - m] % MOD; //C(n,m)*f(n-m)
        cout << ans << endl;
    }
    return 0;
}

 

posted @ 2025-11-18 15:08  hackerchef  阅读(13)  评论(0)    收藏  举报