算法笔记--排列组合

来源:互联网 发布:课时优化八上英语答案 编辑:程序博客网 时间:2024/05/16 01:49

小数据时:C(a,b)
先乘后除不会出现截断以及尽大可能不超数据范围。LL最大C(33,66);

int ans=1;for(int i=0;i<=b;i++){    ans*=(a-i);    ans/=(i+1);}

递推写法(可以取膜)

long long c[1005][1005];void init(){    c[1][1]=1;    for(int i=0;i<=1000;i++) c[i][0]=c[i][i]=1;    for(int i=2;i<=1000;i++)    {        for(int j=1;j<=i;j++)        {            c[i][j]=(c[i-1][j-1]+c[i-1][j])%mod;        }    }}

数据比较大时用Lucas定理:Lucas(a,b)=C(a,b)%p

C(n, m) mod p = n!/(m!(n - m)!) mod p。显然除法取模,这里要用到m!(n-m)!的逆元。

根据费马小定理:

已知(a, p) = 1,则 a^(p-1) ≡ 1 (mod p), 所以 a*a^(p-2) ≡ 1 (mod p)。

也就是 (m!(n-m)!)的逆元为 (m!(n-m)!)^(p-2) ;

#include<cstdio>#include<iostream>#include<cmath>#include<algorithm>#define LL long longusing namespace std;LL n,m,p;LL quick_mod(LL a, LL b){    LL ans = 1;    a %= p;    while(b)    {        if(b & 1)        {            ans = ans * a % p;            b--;        }        b >>= 1;        a = a * a % p;    }    return ans;}LL C(LL n, LL m){    if(m > n) return 0;    LL ans = 1;    for(int i=1; i<=m; i++)    {        LL a = (n + i - m) % p;        LL b = i % p;        ans = ans * (a * quick_mod(b, p-2) % p) % p;    }    return ans;}LL Lucas(LL n, LL m){    if(m == 0) return 1;    return C(n % p, m % p) * Lucas(n / p, m / p) % p;}

写法二

//求C(n,m)%p p(素数)最大为10^5。a,b可以很大!#define LL long longLL PowMod(LL a, LL b, LL MOD){    LL ret = 1;    while (b) {        if (b&1) ret = (ret*a)%MOD;        a = (a*a) % MOD;        b >>= 1;    }    return ret;}LL fac[100005];LL Get_Fact(LL p) {    fac[0] = 1;    for (int i = 1;i <= p; i++)        fac[i] = (fac[i-1]*i)%p;}LL Lucas(LL n, LL m, LL p) {    LL ret = 1;    while (n && m) {        LL a = n%p, b = m%p;        if (a < b) return 0;        ret = (ret*fac[a]*PowMod(fac[b]*fac[a-b]%p, p-2, p))%p;        n /= p; m /= p;    }    return ret;}int main() {    LL n, m, p;    scanf("%I64d %I64d %I64d", &n, &m, &p);    Get_Fact(p);    printf("%I64d\n", Lucas(n, m, p));    return 0;}
原创粉丝点击