zoukankan      html  css  js  c++  java
  • [SDOI2016] 排列计数 (组合数学)

    [SDOI2016]排列计数

    题目描述

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

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

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

    满足条件的序列可能很多,序列数对 10^9+7109+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 = 1000,n leq 8,m leq 8;)

    测试点 4 ~ 6: (T = 1000,n leq 12,m leq 12;)

    测试点 7 ~ 9: (T = 1000,n leq 100,m leq 100;)

    测试点 10 ~ 12:(T = 1000,n leq 1000,m leq 1000;)

    测试点 13 ~ 14:(T = 500000,n leq 1000,m leq 1000;)

    测试点 15 ~ 20:(T = 500000,n leq 1000000,m leq 1000000。)

    Solution

    错排公式/组合计数 裸题
    (Ans=C_{n}^{m} imes D_{n-m}),其中(D_{i})为共i个元素的错排方案数(错排指元素i不在下标为i的位置上)
    然后因为要取模,费马小定理求一下逆元
    在这里摆一下组合及错排的公式

    [C_{n}^{m}=frac{n!}{m! imes{(n-m)!}} ]

    [D_{n}=(n-1) imes(D_{n-1}+D_{n-2}) ]

    其实错排还有一个通项公式,但是由于时间复杂度太高,所以预处理不太常用,但在这里还是摆一下

    [D_{n}=n!(1-frac{1}{1!}+frac{1}{2!}-frac{1}{3!}+...+(-1)^nfrac{1}{n!}) ]

    Code

    #include<bits/stdc++.h>
    #define Min(a,b) (a)<(b)?(a):(b)
    #define Max(a,b) (a)>(b)?(a):(b)
    #define rg register
    #define il inline
    #define lol long long
    
    using namespace std;
    
    const int N=1e6+10,mod=1e9+7;
    
    void in(int &ans) {
    	ans=0; int f=1; char i=getchar();
    	while(i<'0' || i>'9') {if(i=='-') f=-1; i=getchar();}
    	while(i>='0' && i<='9') ans=(ans<<1)+(ans<<3)+i-'0',i=getchar();
    	ans*=f;
    }
    
    int T,n,m;
    lol D[N],ie[N],sum[N];//ie[]是逆元数组,inverse element的简称
    
    lol qpow(lol a,int x,lol ans=1) {
        while(x) {
            if(x&1) ans=ans*a%mod;
            x>>=1,a=a*a%mod;
        }return ans;
    }
    
    il void init() {
        D[0]=D[2]=1;   for(rg int i=3;i<=N;i++) D[i]=(i-1)*(D[i-1]+D[i-2])%mod;
        ie[0]=sum[0]=1; for(rg int i=1;i<=N;i++) sum[i]=sum[i-1]*i%mod,ie[i]=qpow(sum[i],mod-2);
    }
    
    int main()
    {
        in(T); init();
        while(T--) {
            in(n),in(m);
            printf("%lld
    ",D[n-m]*sum[n]%mod*ie[m]%mod*ie[n-m]%mod);
        }
        return 0;
    }
    

    博主蒟蒻,随意转载.但必须附上原文链接

    http://www.cnblogs.com/real-l/

  • 相关阅读:
    乘法逆元
    P1082 同余方程
    数论编程
    倍增LCA模板
    快速幂模板Super
    黑白染色的模板
    暑假提高组集训Day1 T2
    暑假提高组集训Day1 T1
    7月18日刷题记录 二分答案跳石头游戏Getting
    hdu4738(割桥)
  • 原文地址:https://www.cnblogs.com/real-l/p/9645409.html
Copyright © 2011-2022 走看看