zoukankan      html  css  js  c++  java
  • bzoj4517: [Sdoi2016]排列计数--数学+拓展欧几里得

    这道题是数学题,由题目可知,m个稳定数的取法是Cnm

    然后剩下n-m本书,由于编号为i的书不能放在i位置,因此其方法数应由错排公式决定,即D(n-m)

      错排公式:D[i]=(i-1)*(D[i-1]+D[i-2]);

    所以根据乘法原理,答案就是Cnm * D(n-m)

    接下来就是怎么求组合数的问题了

    由于n≤1000000,因此只能用O(n)的算法求组合,这里用乘法逆元(inv[])来辅助求组合数

    即 Cnm = n! / ((n-m)! * m!) = fac[n]*inv[n-m]*inv[m]

    那么乘法逆元是什么呢?

    假设一个数a,且a关于P的乘法逆元为x

    那么 ax≡1 (mod P). 当且仅当 a 与 P 互质时x有解

    简单的说,就是找一个数x,使得(x*a) mod P = 1

    不难得出三者符合 ax+Py=1 (裴蜀定理), y可能是负数

    因此我们可以用拓展欧几里得算出x的值,即为乘法逆元(用inv保存)

    对于求出inv的过程,我们可以不必每次暴力求拓展欧几里得,可由下列递推式O(n)求出

      inv[i]=(i+1)*inv[i+1]

    而D数组只要O(n)推即可,其中D[0]=1, D[1]=0;

    这道题让我明白。。组合数可以O(n)求得,了解了乘法逆元是什么,并且了解到世界上有个叫错排公式的神奇东西Orz

     1 #include<stdio.h>
     2 #include<algorithm>
     3 #include<string.h>
     4 #define LL long long
     5 using namespace std;
     6 const int maxn = 1000005;
     7 const LL MOD = 1e9+7;
     8 int T,n,m;
     9 LL f[maxn],inv[maxn],d[maxn];
    10 
    11 inline void read(int &x){
    12     char c=getchar(); x=0;
    13     while (c<'0' || c>'9') c=getchar();
    14     while (c>='0' && c<='9') x=x*10+c-48, c=getchar();
    15 }
    16 
    17 inline LL ex_gcd(LL &x, LL &y, LL a, LL b){
    18     if (b==0){
    19         x=1; y=0;
    20         return a;
    21     }
    22     LL res=ex_gcd(x,y,b,a%b);
    23     LL t=x; x=y;
    24     y=t-a/b*x;
    25     return res;
    26 }
    27 
    28 inline LL calc(LL a, LL b){
    29     LL x,y;
    30     if (ex_gcd(x,y,a,b) == 1LL)
    31         return (x+b)%b;
    32 }
    33 
    34 int main(){
    35     read(T);
    36     f[0]=1;
    37     for (int i=1; i<=maxn; i++) f[i]=f[i-1] * (LL)i % MOD;
    38     inv[1000000]=calc(f[1000000],MOD);
    39     for (int i=maxn-6; i>=0; i--) inv[i]=inv[i+1] * (LL)(i+1) % MOD;
    40     d[0]=1; d[1]=0; d[2]=1;
    41     for (int i=3; i<=maxn; i++) d[i]=(LL)(i-1)*(d[i-1]+d[i-2]) % MOD;
    42     while (T--){
    43         read(n); read(m);
    44         LL ans=1LL;
    45         //printf("haha %lld %lld %lld %lld
    ", f[n], inv[n-m], inv[m], d[n-m]);
    46         ans=ans*f[n]*inv[n-m] % MOD;
    47         ans=ans*inv[m] % MOD;
    48         ans=ans*d[n-m] % MOD;
    49         printf("%lld
    ", ans);
    50     }
    51     return 0;
    52 }
  • 相关阅读:
    前端插件资源
    wPaint在线绘图插件
    【剑指offer】数字数组中只出现一次(2)
    系统,特别是慢查找
    Asp.Netserver控制发展Grid实现(一个)UI转让
    JAVA连接ACCESS、MYSQL、SQLSEVER、ORACLE数据库
    u_boot启动过程中的具体分析(1)
    免费是移动互联网的第一个念头
    进入公司第五届、六个月
    Windows平台Oracle使用USE_SHARED_SOCKET角色
  • 原文地址:https://www.cnblogs.com/mzl0707/p/5432740.html
Copyright © 2011-2022 走看看