P4071 排列计数

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

题目描述#

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

答案对 109+7 取模

输入格式#

本题单测试点内有多组数据

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

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

对于每组测试数据,每行输入两个整数,依次代表 nm

输出格式#

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

输入输出样例#

输入 #1#

复制5
1 0
1 1
5 2
100 50
10000 5000

输出 #1#

复制0
1
20
578028887
60695423

说明/提示#

数据规模与约定#

本题共 20 个测试点,各测试点等分,其数据规模如下表

测试点编号 T= n,m 测试点编号 T= n,m
13 103 8 1012 103 103
46 103 12 1314 5×105 103
79 103 100 1520 5×105 106

分析#

首先先选出位置与数值相等的 m 个数,那么选择的情况数为 (nm),接着要对剩余的 nm 个数进行错排,我们设 d[i] 表示 i 个数错排的情况数,现在先让每个数都升序排列,接着拿出第一个数字 a[1] ,在后面的 i1 个数字中选择一个与之交换,假设为 a[k],选择的情况数为 i1 种,对于每一种选择的情况,a[k] 已经实现了错排,而交换后的 a[1] 有两种选择,第一种是就安放原位,那么它也实现了错排,接着只需对 i2 个数进行错排即可,第二种情况是它不放在这,那么它还要参与接下来的错排操作,所以要对 i1 个数进行错排,根据加法原理,两种情况的方案数为 d[i1]+d[i2],又根据乘法原理,d[i]=(n1)(d[i1]+d[i2])

接下来考虑时间复杂度,题目中 N,M106,这样组合数就不能通过递推得到,而应该用组合数公式计算:

(nm)=n!m!(nm)!n!m!(nm)!n!(m!)1(nm)!1(modP)

所以我们要提前计算 i 的阶乘对 P 取模的结果以及其乘法逆元

由于模数 P=109+7 是质数,所以可以根据费马小定理来求乘法逆元:

aP1aaP21(modP)a1aP2(modP)

这样预处理阶乘和递推错排情况数的时间复杂度均为 O(N),计算每个阶乘的乘法逆元要使用快速幂,时间复杂度为 O(Nlog2N)

代码部分#

代码中使用 fac[i] 表示 i!modPinv[i] 表示 (i!)1modPd[i] 表示 i 个数的错排情况数

复制#include <cstdio>
using namespace std;

const int N=1e6+10,P=1e9+7;
int t,n,m;
long long ans,fac[N],inv[N],d[N];

long long power_mod(int x,int y)
{
    if(y==0) return 1;
    long long res=power_mod(x,y>>1);
    res=(res*res)%P;
    if(y&1) res=(res*x)%P;
    return res;
}

int main()
{
    fac[0]=1;
    for(int i=1;i<=1e6;i++) fac[i]=fac[i-1]*i%P;
    d[1]=0,d[2]=1;
    for(int i=3;i<=1e6;i++) d[i]=(i-1)*(d[i-1]+d[i-2])%P;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&n,&m);
        if(n==m) // special judge
        {
            puts("1");
            continue;
        }
        if(inv[m]==0) inv[m]=power_mod(fac[m],P-2);
        if(inv[n-m]==0) inv[n-m]=power_mod(fac[n-m],P-2);
        ans=fac[n]*inv[m]%P*inv[n-m]%P*d[n-m]%P;
        printf("%lld\n",ans);
    }
    return 0;
}
posted @   fenggwsx  阅读(75)  评论(0编辑  收藏  举报
编辑推荐:
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
点击右上角即可分享
微信分享提示
主题色彩