码迷,mamicode.com
首页 > 其他好文 > 详细

bzoj4517: [Sdoi2016]排列计数--数学+拓展欧几里得

时间:2016-04-25 22:47:04      阅读:226      评论:0      收藏:0      [点我收藏+]

标签:

这道题是数学题,由题目可知,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

 

#include<stdio.h>
#include<algorithm>
#include<string.h>
#define LL long long
using namespace std;
const int maxn = 1000005;
const LL MOD = 1e9+7;
int T,n,m;
LL f[maxn],inv[maxn],d[maxn];

inline void read(int &x){
    char c=getchar(); x=0;
    while (c<0 || c>9) c=getchar();
    while (c>=0 && c<=9) x=x*10+c-48, c=getchar();
}

inline LL ex_gcd(LL &x, LL &y, LL a, LL b){
    if (b==0){
        x=1; y=0;
        return a;
    }
    LL res=ex_gcd(x,y,b,a%b);
    LL t=x; x=y;
    y=t-a/b*x;
    return res;
}

inline LL calc(LL a, LL b){
    LL x,y;
    if (ex_gcd(x,y,a,b) == 1LL)
        return (x+b)%b;
}

int main(){
    read(T);
    f[0]=1;
    for (int i=1; i<=maxn; i++) f[i]=f[i-1] * (LL)i % MOD;
    inv[1000000]=calc(f[1000000],MOD);
    for (int i=maxn-6; i>=0; i--) inv[i]=inv[i+1] * (LL)(i+1) % MOD;
    d[0]=1; d[1]=0; d[2]=1;
    for (int i=3; i<=maxn; i++) d[i]=(LL)(i-1)*(d[i-1]+d[i-2]) % MOD;
    while (T--){
        read(n); read(m);
        LL ans=1LL;
        //printf("haha %lld %lld %lld %lld\n", f[n], inv[n-m], inv[m], d[n-m]);
        ans=ans*f[n]*inv[n-m] % MOD;
        ans=ans*inv[m] % MOD;
        ans=ans*d[n-m] % MOD;
        printf("%lld\n", ans);
    }
    return 0;
}

 

bzoj4517: [Sdoi2016]排列计数--数学+拓展欧几里得

标签:

原文地址:http://www.cnblogs.com/mzl120918/p/5432740.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!