您的位置:首页 > 其它

数论总结之Lucas

2017-06-10 11:08 190 查看
Lucas定理

主要用来处理大组合数取模(模数是质数)的问题

内容:C(n,m)=C(n/p,m/p)*C(n%p,m%p)%p (n/p依旧是向下取整)

证明:Lucas定理的另一种表现方式:

C(sp+q,tp+r)≡C(s,t)*C(q,r)(mod p)

首先:1+x^(sp+q)=((1+x)^p)^s*(1+x)^q

所以:(1+x)^(sp+q)≡Σi=0…s C(s,i)x^(i*p)Σ(i=0…q)C(q,i)*x^i (利用二项式定理)

令右式i=t,j=r则右式x^(tp+r)系数为 C(s,t)*C(q,r);

对左式直接使用二项式定理

令左式i=tp+r,则左式x^(tp+r)系数为C(sp+q,tp+r);

所以得证。

关于Lucas定理的扩展:

对于模数p

1.p是素数

2.p不是素数

又可分为(1).分解完质数后每个质因子只出现一次

(2).分解完质数后每个质因子出现不止一次

3.p不是素数但p=m^t(m为素数)

处理方式:

对1 直接运用Lucas定理即可求解

对2(1) 可以运用Lucas求对p的每个质因子的模,再运用中国剩余定理将模数合并(又引出了一个flag)

对2(2) 对出现不止一次的质因子,则每个质因子的n次幂跟3一样处理,然后再运用中国剩余定理合并

对3

因为C(n,m)=n!/(m!(n-m)!)

所以我们可以分为三部分求解:n!%p^t,m!%p^t,(n-m)!%p^t 再逆元就可求解。

·求n!%p^t:

1.求p的幂,即p^(n/p) (快速幂)

2.新的阶乘 即(n/p)! (递归)

3.剩下部分 模p^t意义下以p^t为周期,所以求p^t长度就可。

剩下孤立的数长度不会超过p^t,暴力求解就可。

·关于m!%p^t和(n-m)!%(p^t)

方法大致与上述相同 但因为不互质就无法求逆元,所以应先除去质因子,求出逆元后再乘回去。

补充:

计算n!的质因子p的个数x为:

x=(n/p)+(n/(p^2))+(n/(p^3))+…

递推:f
=f[n/p]+n/p; //n/p均为向下取整

题【板】:

hdu 3037[感觉我做的题都还是挺水的]

[这道题是好久以前做的都快不记得题面了]

[也当留个板吧]

//玄学 long long
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#define ll long long
using namespace std;

int t,n,m,p;

ll exp_mod(ll a,ll b,ll p){
ll res=1;
for(res;b;b>>=1,a=a*a%p){
if(b&1) res=res*a%p;
}
return res;
}

ll cmp(ll a,ll b,ll p) {
if(a<b)   return 0;
if(a==b)  return 1;
if(b>a-b)  b=a-b;
ll ans=1,ca=1,cb=1;
for(int i=0;i<b;++i) {
ca=(ca*(a-i))%p;
cb=(cb*(b-i))%p;
}
ans=(ca*exp_mod(cb,p-2,p))%p;
return ans;
}

ll Lucas(ll n,ll m,ll p) {
ll ans=1;
while(n&&m&&ans) {
ans=(ans*cmp(n%p,m%p,p))%p;
n/=p;
m/=p;
}
return ans;
}

int main(){
scanf("%d",&t);
while(t--){
scanf("%d%d%d",&n,&m,&p);
printf("%d\n",Lucas(n+m,m,p));
}
return 0;
}


大概看了一下代码,其实就是跟Lucas的内容差不多,递归实现,但是具体实现有一些要注意的小细节而已。

【下面的内容有毒】

如果在考试的时候,出现了一道考Lucas的题目,但是已经很久没有打过Lucas了,也特别担心自己Lucas写炸,那怎么办。。

这个时候我们也可以根据组合数的计算方式暴力用求逆元水过去2333

沈阳集训Day2T1

题面:计算C(m,n) mod 9901 的值

数据范围:n<=m<=20000

【就是这么一道水题】【我还将它变得更水了23333】

【我估计整个班只有我一个人是这样做的。。。】

代码如下【每个部分都清楚易懂】:

#include<cstdio>
#include<algorithm>
#define ll long long
using namespace std;

const int mod = 9901;
const int INF = 1<<30;

int m,n;
int tot=0;

ll mul(int x,int d){
ll ans=1,xx=1;
for(register int i=2;i<=x;i++){
if(d==0&&i%mod==0){
xx*=i;tot++;continue;}
if(d==1&&i%mod==0){
if(tot>=1){
tot--;
continue;}}
ans*=i;
ans%=mod;
}
return ans;
}

int inverse(ll x){
if(x==0) return 0;
for(register int i=1;i<=INF;i++)
if((x*i)%mod == 1) {return i;}
}

ll C(int m,int n){
int xx=inverse(mul(n,0));
int xxx=inverse(mul(m-n,0));
//  printf("%lld %d ",mul(m-n),xxx);
ll ans=((mul(m,1)%mod)*(xx%mod))%mod;
return (ans%mod)*(xxx%mod)%mod;
}

int main(){
freopen("comb.in","r",stdin);
freopen("comb.out","w",stdout);
scanf("%d%d",&m,&n);
printf("%lld",C(m,n));
return 0;
}


在考试的时候即使觉得自己打不出来正解,也不要轻言放弃

有很多其他杂七杂八的算法

万一就水过去了呢233333
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: