• P4071 [SDOI2016]排列计数


    题目描述

    求有多少种长度为 n 的序列 A,满足以下条件:

    1 ~ n 这 n 个数在序列中各出现了一次

    若第 i 个数 A[i] 的值为 i,则称 i 是稳定的。序列恰好有 m 个数是稳定的

    满足条件的序列可能很多,序列数对 109+7 取模。

    输入输出格式

    输入格式:

    第一行一个数 T,表示有 T 组数据。

    接下来 T 行,每行两个整数 n、m。

    输出格式:

    输出 T 行,每行一个数,表示求出的序列数

    输入输出样例

    输入样例#1: 
    5
    1 0
    1 1
    5 2
    100 50
    10000 5000
    输出样例#1: 
    0
    1
    20
    578028887
    60695423

    说明

    测试点 1 ~ 3: T=1000n≤8m≤8

    测试点 4 ~ 6: T=1000n≤12m≤12

    测试点 7 ~ 9: T=1000n≤100m≤100

    测试点 10 ~ 12:T=1000n≤1000m≤1000

    测试点 13 ~ 14:T=500000n≤1000m≤1000

    测试点 15 ~ 20:T=500000n≤1000000m≤1000000

    Solution:

      本题组合数学+错排公式+线推逆元。

      组合数学和逆元就不说了,介绍下错位排列。

      错位排列,顾名思义就是一个n元排列,每个元素不能排在自己的位置上的方案数,一般记作$D(n)$。

      通项公式:

        $$D_n=n! imes(1-frac{1}{1!}+frac{1}{2!}-frac{1}{3!}…frac{(-1)^n}{n!})$$

       证明:

       设$S$是由$1,2,…n$构成的所有全排列组成的集合,则$|S|=n!$。

       设$A_i$是在$1,2,…n$的所有排列种由第$i$个位置上的元素恰好是$i$的所有排列组成的集合,则有:$|A_i|=(n-1)!$。

       同理可得:$|A_icap A_j|=(n-2)!$

       ……

       一般情况下有:$|A_{i1}cap A_{i2}cap …cap A_{ik}|=(n-k)!$。

       因为$D_n$是$S$中不满足性质$P_1,P_2,…,P_n$的元素个数,所以由容斥原理的:

       $D_n=|overline A_1cap overline A_2 …cap overline A_n|$

         $=n!-C(n,1)*(n-1)!+C(n,2)*(n-2)!-…(-1)^nC(n,n)*0!$

         $=n! imes(1-frac{1}{1!}+frac{1}{2!}-…frac{(-1)^n}{n!})$

      递推公式:

        $$D_n=(n-1) imes(D_{n-1}+D_{n-2})$$

       证明:

       第一步,把第$n$个元素放在一个位置,比如位置$k$,一共有$n-1$种方法;

       第二步,放编号为$k$的元素,这时有两种情况:(1)把它固定到位置$n$,由于第$n$个元素固定到了位置$k$,剩下$n-2$个元素就有$D_{n-2}$种方法;(2)第$k$个元素不能放到位置$n$,而第$n$个元素固定到了位置$k$,于是$n-1$个元素,有$D_{n-1}$种方法;

       综上得到$D_n = (n-1) imes(D_{n-2} + D_{n-1})$,特殊地,$D_1=0, D_2=1$。

      当然更为常用的是后面的递推公式,比如本题。

      不难发现本题答案为$C(n,m) imes D(n-m)$。

      于是我们只要预处理出$10^6$内的阶乘取模、阶乘的逆元、错排的方案数就好了。

    代码:

    /*Code by 520 -- 9.14*/
    #include<bits/stdc++.h>
    #define il inline
    #define ll long long
    #define RE register
    #define For(i,a,b) for(RE int (i)=(a);(i)<=(b);(i)++)
    #define Bor(i,a,b) for(RE int (i)=(b);(i)>=(a);(i)--)
    using namespace std;
    const ll N=1000005,mod=1e9+7;
    int n,m;
    ll d[N],c[N],inv[N];
    
    int gi(){
        int a=0;char x=getchar();
        while(x<'0'||x>'9')x=getchar();
        while(x>='0'&&x<='9')a=(a<<3)+(a<<1)+(x^48),x=getchar();
        return a;
    }
    
    il void Pre(){
        d[1]=0,d[2]=1;
        For(i,3,1000000) d[i]=(i-1)*(d[i-1]+d[i-2])%mod;
        c[0]=1,c[1]=1,inv[1]=1;
        For(i,2,1000000) c[i]=c[i-1]*i%mod,inv[i]=(mod-mod/i*inv[mod%i]%mod)%mod;
        For(i,2,1000000) inv[i]=inv[i]*inv[i-1]%mod;
    }
    
    int main(){
        Pre();
        int T=gi();
        while(T--) {
            n=gi(),m=gi();
            if(n-m==1) printf("0
    ");
            else if(n==m) printf("1
    ");
            else if(!m) printf("%lld
    ",d[n]);
            else printf("%lld
    ",c[n]*inv[m]%mod*inv[n-m]%mod*d[n-m]%mod);
        }
        return 0;
    }
  • 相关阅读:
    Deep Learning入门
    基本技能(一)
    NNCRF之NNSegmentation, NNPostagging, NNNameEntity
    word2vector 使用方法 计算语义相似度
    Berkeley parser使用方法
    zpar使用方法之Chinese Word Segmentation
    【delphi】delphi出现‘尚未调用CoInitialize’异常
    VS05 VS08 VS10 工程之间的转换
    odbc数据源for mysql
    【delphi】Delphi过程、函数传递参数的八种方式
  • 原文地址:https://www.cnblogs.com/five20/p/9651395.html
Copyright © 2020-2023  润新知