fzu 2020 组合 组合数对素数取余
2011-10-10 12:03
351 查看
1)根据一下公式,直接计算
C(n,m) mod p = n*``*(n-m+1)/(1*``*m) mod p
计算分别分子nn、分母mm中p的个数和对p的余数,若分子中p的个数多余分母中p的个数,则结果为0,
若不是,则原式变为nn/mm mod p (nn,p)=1,(mm,p)=1
此时如何求逆元变得至关重要,以下有两种解法。
2) Lucas 定理:是用来求 C(n,m) mod p的值,p是素数。
描述为:
Lucas(n,m,p)=C(n%p,m%p)* Lucas(n/p,m/p,p)
Lucas(n,0,p)=1;
而
C(a,b)=a*(a-1)*```*(a-b+1)/(1*2*```*b)%p,
其中分子为aa分母为bb,故C(a,b)=aa/bb%p,(此时求法同上)
至此逆元的求法也很重要,逆元的求法,如下会有所解释。
补充知识:逆元的求法
(a/b) mod p=a*(b逆) mod p
b*x=1(mod p) x就是b的逆元
而b逆可以利用扩展欧几里德或欧拉函数求得:
1).扩展欧几里德:b*x+p*y=1 有解,x就是所求
2).欧拉函数:b^(p-1)=1(mod p),故b*b^(p-2)=1(mod p),所以x=b^(p-2)
综上所述,其实方法一可以看成是暴力计算,而方法二中只是将大数变为数,再采用方法一的方法计算,这样时间上会有所降低。
方法一:直接计算,代码如下:
根据求逆元的不同也可以对其中的若干方法进行改写如下所示:
方法二:利用Lucas 定理,代码如下所示:
根据求逆元的不同,以上若干方法又可以改写为:
总结:此类问题,涉及到的方法有,求逆元的方法(扩展欧几里德算法,欧拉函数的知识(有涉及到大数取余)),Lucas定理,小组合数取余。
C(n,m) mod p = n*``*(n-m+1)/(1*``*m) mod p
计算分别分子nn、分母mm中p的个数和对p的余数,若分子中p的个数多余分母中p的个数,则结果为0,
若不是,则原式变为nn/mm mod p (nn,p)=1,(mm,p)=1
此时如何求逆元变得至关重要,以下有两种解法。
2) Lucas 定理:是用来求 C(n,m) mod p的值,p是素数。
描述为:
Lucas(n,m,p)=C(n%p,m%p)* Lucas(n/p,m/p,p)
Lucas(n,0,p)=1;
而
C(a,b)=a*(a-1)*```*(a-b+1)/(1*2*```*b)%p,
其中分子为aa分母为bb,故C(a,b)=aa/bb%p,(此时求法同上)
至此逆元的求法也很重要,逆元的求法,如下会有所解释。
补充知识:逆元的求法
(a/b) mod p=a*(b逆) mod p
b*x=1(mod p) x就是b的逆元
而b逆可以利用扩展欧几里德或欧拉函数求得:
1).扩展欧几里德:b*x+p*y=1 有解,x就是所求
2).欧拉函数:b^(p-1)=1(mod p),故b*b^(p-2)=1(mod p),所以x=b^(p-2)
综上所述,其实方法一可以看成是暴力计算,而方法二中只是将大数变为数,再采用方法一的方法计算,这样时间上会有所降低。
方法一:直接计算,代码如下:
#include<stdio.h> #define LL long long int pnum, x, y; int getMultMod(int start, int end, int p) { int i, j; LL res; for (i = start, res = 1, pnum = 0; i <= end; i++) { if (i % p == 0) { j = i; while (j % p == 0) { pnum++; j /= p; } res = res * j % p; } else { res = res * i % p; } } return (int) (res % p); } void extend_gcd(int a, int b) { int xx; if (b == 0) { x = 1, y = 0; return; } extend_gcd(b, a % b); xx = x; x = y, y = xx - a / b * y; } void solve(int n, int m, int p) { int a, b, apnum, bpnum; LL res; a = getMultMod(n - m + 1, n, p); apnum = pnum; b = getMultMod(1, m, p); bpnum = pnum; if (apnum > bpnum) { puts("0"); return; } else { extend_gcd(b, p); res = (LL) x; res = (res % p + p) % p; res = res * a % p; printf("%I64d\n", res); } } int main() { #ifndef ONLINE_JUDGE freopen("data.in", "r", stdin); #endif int t, n, m, p; scanf("%d", &t); while (t--) { scanf("%d %d %d", &n, &m, &p); solve(n, m, p); } return 0; }
根据求逆元的不同也可以对其中的若干方法进行改写如下所示:
int modular_exp(int a, int b, int p) { LL res, temp; res = 1 % p, temp = a % p; while (b) { if (b & 1) { res = res * temp % p; } temp = temp * temp % p; b >>= 1; } return (int) res; } void solve(int n, int m, int p) { int a, b, apnum, bpnum; LL res; a = getMultMod(n - m + 1, n, p); apnum = pnum; b = getMultMod(1, m, p); bpnum = pnum; if (apnum > bpnum) { puts("0"); return; } else { res = modular_exp(b, p - 2, p); res = res * a % p; printf("%I64d\n", res); } }
方法二:利用Lucas 定理,代码如下所示:
#include<stdio.h> #define LL long long int x, y; void extend_gcd(int a, int b) { int xx; if (b == 0) { x = 1, y = 0; return; } extend_gcd(b, a % b); xx = x; x = y, y = xx - a / b * y; } int C(int a, int b, int p) { int i; LL resa, resb, res; if (b > a) { return 0; } for (i = 0, resa = 1, resb = 1; i < b; i++) { resa = resa * (a - i) % p, resb = resb * (b - i) % p; } extend_gcd(resb, p); res = (LL) x; res = (res % p + p) % p; res = res * resa % p; return (int) res; } void solve(int n, int m, int p) { int a, b; LL res; res = 1; while (n || m) { a = n % p, b = m % p; res = res * C(a, b, p) % p; n /= p, m /= p; } printf("%I64d\n", res); } int main() { #ifndef ONLINE_JUDGE freopen("data.in", "r", stdin); #endif int t, n, m, p; scanf("%d", &t); while (t--) { scanf("%d %d %d", &n, &m, &p); solve(n, m, p); } return 0; }
根据求逆元的不同,以上若干方法又可以改写为:
int modular_exp(int a, int b, int p) { LL res, temp; res = 1 % p, temp = a % p; while (b) { if (b & 1) { res = res * temp % p; } temp = temp * temp % p; b >>= 1; } return (int) res; } int C(int a, int b, int p) { int i; LL resa, resb, res; if (b > a) { return 0; } for (i = 0, resa = 1, resb = 1; i < b; i++) { resa = resa * (a - i) % p, resb = resb * (b - i) % p; } res = modular_exp(resb, p - 2, p); res = res * resa % p; return (int) res; }
总结:此类问题,涉及到的方法有,求逆元的方法(扩展欧几里德算法,欧拉函数的知识(有涉及到大数取余)),Lucas定理,小组合数取余。
相关文章推荐
- FZU 2020 组合(组合数 lucas定理模板题)
- FZU 2020 组合 Lucas的应用
- 【FZU 2015 && FZU 2020 】 组合+大数取模
- hdu 3037 Saving Beans fzu 2020 组合 hit 2813 Garden visiting hrbeu 组合数 fzu 1564 Combination
- FZU 2020 组合(Lucas定理)
- FZU 2020 组合 [ Lucas ]
- FZU - 2020 组合
- FZU 2020 组合(扩展欧几里得)
- FZU 2020 组合(Lucas定理)
- FZU 2020 组合 (Lucas定理)
- FZU 2020 组合
- FZU 2020 组合
- FOJ 2020 组合(组合数取素数摸模板:Lucas)
- FZU 2020 组合数求模
- FZU 2020 组合(Lucas定理的应用)
- fzu2020( c(n,m)%p,其中n, m, p (1 <= m <= n <= 10^9, m <= 10^4, m < p < 10^9, p是素数) )
- fzu 2020 组合数取模
- 【Lucas组合数定理】组合-FZU 2020
- FZU 2020 组合
- FZU 2020 :组合 【lucas】