LightOJ - 1067 数论<100000左右的组合数取模求法《逆元》>
2016-08-06 21:54
453 查看
题目链接: LightOJ - 1067
对于组合数取模的问题----数值10e6且询问的次数很多的情况--我们可以通过打表降低时间复杂度
先说一下:C(n,m)= n!/(m!*(n-m)!)-----
C(n ,m)=(n*(n-1)*....*(n-m+1))/(1*2*..........*m)=(n*(n-1)*(n-2)*....1)/((n-m)*(n-m-1)*...*1)/(1*2*...*m)= N! / M! /(N-M)!
打一个N!取模表--再打一个N!对模的逆元表--
对于(A/B)mod C 的问题,直接(A mod C)/ (B mod C)是错误的: ( 16/ 8 ) % 4 == 2 而 (16%4)/(8%4)无意义--
逆元 AB==1(mod)C
A关于C的逆元为B-
B关于C的逆元为A-
即关于对C取模时---A*B=1;
B=1/A;
当我们求S/A(mod)C时---我们就可以求S*B(mod)C了---
求逆元可以用拓展欧几里德--
在此题还可以用费马小定理
假如p是质数,且gcd(a,p)=1,那么
a(p-1)≡1(mod
p)。即:假如a是整数,p是质数,且a,p互质(即两者只有一个公约数1),那么a的(p-1)次方除以p的余数恒等于1。
a(p-1)≡1(mod
p)所以--- a * a(p-2)≡1(mod
p)----即 a 关于 P 的逆元为 *a(p-2) 然后快速幂就行了...
用费马小定理+快速幂在此题时间上有优势:
![](https://img-blog.csdn.net/20160806220930460?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
拓展欧几里德求逆元:
费马小定理+快速幂求逆元:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define LL long long
#define MA 1000100
#define mod 1000003
LL pri[MA];
LL ni[MA],ans;
LL pp(LL xx,int k)
{
LL lp=1,huan=xx;
while (k)
{
if (k%2)
lp=(lp*huan)%mod;
huan=(huan*huan)%mod;
k/=2;
}
return lp;
}
void s()
{
pri[0]=1;LL x,y;
ni[0]=1;
for (int i=1;i<MA;i++)
{
pri[i]=pri[i-1]*i%mod;
ni[i]=pp(pri[i],mod-2);
}
}
int main()
{
s();
int t,n,k,ca=1;scanf("%d",&t);
while (t--)
{
scanf("%d%d",&n,&k);
ans=((pri
*ni[k]%mod)*ni[n-k])%mod;
printf("Case %d: %lld\n",ca++,ans);
}
return 0;
}
对于组合数取模的问题----数值10e6且询问的次数很多的情况--我们可以通过打表降低时间复杂度
先说一下:C(n,m)= n!/(m!*(n-m)!)-----
C(n ,m)=(n*(n-1)*....*(n-m+1))/(1*2*..........*m)=(n*(n-1)*(n-2)*....1)/((n-m)*(n-m-1)*...*1)/(1*2*...*m)= N! / M! /(N-M)!
打一个N!取模表--再打一个N!对模的逆元表--
对于(A/B)mod C 的问题,直接(A mod C)/ (B mod C)是错误的: ( 16/ 8 ) % 4 == 2 而 (16%4)/(8%4)无意义--
逆元 AB==1(mod)C
A关于C的逆元为B-
B关于C的逆元为A-
即关于对C取模时---A*B=1;
B=1/A;
当我们求S/A(mod)C时---我们就可以求S*B(mod)C了---
求逆元可以用拓展欧几里德--
在此题还可以用费马小定理
假如p是质数,且gcd(a,p)=1,那么
a(p-1)≡1(mod
p)。即:假如a是整数,p是质数,且a,p互质(即两者只有一个公约数1),那么a的(p-1)次方除以p的余数恒等于1。
a(p-1)≡1(mod
p)所以--- a * a(p-2)≡1(mod
p)----即 a 关于 P 的逆元为 *a(p-2) 然后快速幂就行了...
用费马小定理+快速幂在此题时间上有优势:
拓展欧几里德求逆元:
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; #define LL long long #define MA 1000100 #define mod 1000003 LL pri[MA]; LL ni[MA],ans; LL extend(LL a,LL b,LL &x,LL &y) { if (b==0) { x=1;y=0; return a; } else { LL t=extend(b,a%b,y,x); y-=x*(a/b); return t; } } void s() { pri[0]=1;LL x,y; ni[0]=1; for (int i=1;i<MA;i++) { pri[i]=pri[i-1]*i%mod; extend(pri[i],mod,x,y); ni[i]=(x%mod+mod)%mod; } } int main() { s(); int t,n,k,ca=1;scanf("%d",&t); while (t--) { scanf("%d%d",&n,&k); ans=((pri *ni[k]%mod)*ni[n-k])%mod; printf("Case %d: %lld\n",ca++,ans); } return 0; }
费马小定理+快速幂求逆元:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define LL long long
#define MA 1000100
#define mod 1000003
LL pri[MA];
LL ni[MA],ans;
LL pp(LL xx,int k)
{
LL lp=1,huan=xx;
while (k)
{
if (k%2)
lp=(lp*huan)%mod;
huan=(huan*huan)%mod;
k/=2;
}
return lp;
}
void s()
{
pri[0]=1;LL x,y;
ni[0]=1;
for (int i=1;i<MA;i++)
{
pri[i]=pri[i-1]*i%mod;
ni[i]=pp(pri[i],mod-2);
}
}
int main()
{
s();
int t,n,k,ca=1;scanf("%d",&t);
while (t--)
{
scanf("%d%d",&n,&k);
ans=((pri
*ni[k]%mod)*ni[n-k])%mod;
printf("Case %d: %lld\n",ca++,ans);
}
return 0;
}
相关文章推荐
- poj 2154 Color < 组合数学+数论>
- POJ 1845 Sumdiv <数论(逆元 / 二分递归)>
- poj 1845 Sumdiv <数论>
- 【LightOJ】1067 - Combinations(Lucas & 逆元)
- java学习笔记<六>继承与组合
- UVALive 7040 Color (容斥原理 + 组合数学递推公式 + 求逆元 + 基础数论)
- HDOJ 1319 Prime Cuts<数论>
- LightOJ 1245 Harmonic Number (II) <思维---数学>
- <marquee> 标签实现文字左右滚动
- 简单组合java.util.Map<K,V>实现Map<K,P,V>
- [原]左右的移动<<>><>jQuery的实现
- 自定义View<1> 多个控件进行组合形成新的自定义控件
- Dictionary、List<KeyValuePair<i>>的组合使用
- LightOJ - 1067 Combinations 大组合数取模(费马小定理与逆元)
- LightOJ 1067 Combinations 组合数取模
- SQL 必知必会·笔记<12>组合查询
- Android 使用RxJava+Retrofit +Realm 组合加载数据 <读取缓存 显示 请求网络数据 缓存最新数据 更新界面>(二)
- 简单组合java.util.Map<K,V>实现Map<K,P,V>
- 左右的移动<<>><>jQuery的实现
- android自定义组合控件之<ImgView><TextView>