P4071 [SDOI2016]排列计数

题目地址

题目大意:求n的错位排序数

有全排列1n,{1,2,3,,n}对于a[i]i
我们称为错位排列
有递推式

Dn=(n1)(Dn1+Dn2)

证明:
设有n的全排列{1,2,3,4,n}
n=1Dn为0
n=2Dn为1
设最后1位为xn
nn>2,k(kn)n在第k

  1. kn上有 D(n2)种错排
  2. k不排在第n位时,那么将第n位重新考虑成一个新的“第k位”,这时的包括k在内的剩下n1个数的每一种错排,都等价于只有n1个数时的错排(只是其中的第k位会换成第n位)。其错排数为Dn1
    因为kn1种取法所以Dn=(n1)(Dn1+Dn2)
#include <iostream>

using namespace std;

typedef long long ll;
ll f[25];
int main()
{
    int n;
    scanf("%d",&n);
    f[1] = 0,f[2] = 1;
    for(int i = 3 ; i <= n ; i ++ )
    {
        f[i] = (i - 1)*(f[i - 1] + f[i - 2]);
    }
    printf("%lld",f[n]);
}

然后我们就可写下面一道题了 [SDOI2016]排列计数

求有多少种 1n 的排列 a,满足序列恰好有 m 个位置 i,使得 ai=i
答案对 109+7 取模。

题目大意:
题意是让长度为n的序列有且仅有有m个数在原本的位置上.

也就是求除nm的错位排序数乘上m个数的位置Cmn
Cmn×Dnm

通过预处理阶乘逆元和错位排序数,在进行查表操作即可

#include <iostream>
#include <algorithm>
#include <cmath>

using namespace std;

typedef long long ll;

const int N = 1000010;
const int mod = 1e9 + 7;
ll db[N];
ll invfact[N],fact[N];


int power(int a,int b,int p)
{
    int res = 1;
    while(b)
    {
        if(b&1)res = (ll)res * a % p;
        a = (ll) a * a % p;
        b >>= 1;
    }
    return res;
}

void facts()
{
    fact[0] = invfact[0] = 1;
    for(int i = 1 ; i <= N ; i ++ )
    {
        fact[i] = (ll)fact[i - 1] * i % mod;
        invfact[i] = (ll)invfact[i - 1] * power(i,mod - 2,mod) % mod;
    }
}


void dbs()
{
    db[1] = 0,db[2] = 1;
    for(int i = 3 ; i <= N ; i ++ )
    {
        db[i] =(i - 1)*(db[i - 1] + db[ i - 2 ]) % mod;
    }
}




int main()
{
    
    int T,n,m;
    scanf("%d",&T);
    facts();
    dbs();
    while(T--)
    {
        scanf("%d%d",&n,&m);
        if(n == m){printf("1\n");continue;}
                    
        ll cnm = (ll)fact[n] * invfact[m] % mod * invfact[n - m] % mod;
        printf("%lld\n",(ll)cnm * db[n-m]%mod);

    }

}
posted @   Erfu  阅读(42)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 【杂谈】分布式事务——高大上的无用知识?
点击右上角即可分享
微信分享提示