Lucas定理

  A、B是非负整数,p是质数。AB写成p进制:A=a[n]a[n-1]...a[0],B=b[n]b[n-1]...b[0]。

  则组合数C(A,B)与C(a[n],b[n])*C(a[n-1],b[n-1])*...*C(a[0],b[0])  modp同

  即:Lucas(n,m,p)=c(n%p,m%p)*Lucas(n/p,m/p,p)

  以求解n! % p为例,把n分段,每p个一段,每一段求的结果是一样的。但是需要单独处理每一段的末尾p, 2p, ...,把p提取出来,会发现剩下的数正好又是(n / p)!,相当于划归成了一个子问题,这样递归求解即可。这个是单独处理n!的情况,当然C(n,m)就是n!/(m!*(n-m)!),每一个阶乘都用上面的方法处理的话,就是Lucas定理了,注意这儿的p是素数是有必要的。

Lucas最大的数据处理能力是p在10^5左右,不能再大了,hdu 3037就是10^5级别的!

  未写main函数

const maxn=;

var n,m:int64;
fac:array [..maxn] of int64; function quickmod(a,b,mol:int64):int64;
var ans:int64;
begin
ans:=;
while b<> do
begin
if (b and )= then ans:=(ans*a) mod mol;
a:=a*a mod mol;
b:=b>>;
end;
exit(ans);
end; function get_fact(p:int64):int64;
var i:longint;
begin
fac[]:=;
for i:= to p do
fac[i]:=(fac[i-]*i) mod p;
end; function lucas(n,m,p:int64):int64;
var ans,aa,bb:int64;
begin
ans:=;
while (a>) and (k>) do
begin
aa:=a mod p;
bb:=b mod p;
if aa<bb then exit();
ans:=ans*fac[aa]*quickmod(fac[bb]*fac[aa-bb] mod p,p-,p) mod p;
a:=a div p;
k:=k div p;
end;
exit(ans);
end;

  当p很大怎么搞?显然这样直接开数组会MLE

  我们可以这样做。

  分子相乘取模,分母相乘取模

  对分母求逆元,分子乘逆元取模

  给个代码

  

const mol=;

var n,m:longint;

function inv(a,p:int64):int64;
var b,c,q,k1,k2,k3:int64;
begin
b:=p;
c:=a mod b;
q:=a div b;
k1:=;
k2:=;
k3:=(k1+p-q*k2 mod p) mod p;
while (c xor )<> do
begin
a:=b;
b:=c;
c:=a mod b;
q:=a div b;
k1:=k2;
k2:=k3;
k3:=(k1+p-q*k2 mod p) mod p;
end;
exit(k3);
end; function calc(x,y:int64):int64;
var i,j,ans,up,down:int64;
begin
i:=y+;
j:=x;
up:=;
down:=;
while i<=j do
begin
up:=up*i mod mol;
inc(i);
end;
i:=x-y;
while i>= do
begin
down:=down*i mod mol;
dec(i);
end;
ans:=inv(down,mol);
ans:=up*ans mod mol;
exit(ans);
end; begin
read(n,m);
writeln(calc(n+m,m));
end.

再来说逆元:

对于正整数,如果有,那么把这个同余方程中的最小正整数解叫做的逆元。

逆元一般用扩展欧几里得算法来求得,如果为素数,那么还可以根据费马小定理得到逆元为

推导过程如下

求现在来看一个逆元最常见问题,求如下表达式的值(已知

当然这个经典的问题有很多方法,最常见的就是扩展欧几里得,如果是素数,还可以用费马小定理。

但是你会发现费马小定理和扩展欧几里得算法求逆元是有局限性的,它们都会要求互素。实际上我们还有一

种通用的求逆元方法,适合所有情况。公式如下

现在我们来证明它,已知,证明步骤如下

接下来来实战一下,看几个关于逆元的题目。(转自ACdreamer犇的blog)

题目:http://poj.org/problem?id=1845

题意:给定两个正整数,求的所有因子和对9901取余后的值。

分析:很容易知道,先把分解得到,那么得到,那么

的所有因子和的表达式如下

  

第二种方法就是用等比数列求和公式,但是要用逆元。用如下公式即可

因为可能会很大,超过int范围,所以在快速幂时要二分乘法。

其实有些题需要用到的所有逆元,这里为奇质数。那么如果用快速幂求时间复杂度为

如果对于一个1000000级别的素数,这样做的时间复杂度是很高了。实际上有的算法,有一个递推式如下

它的推导过程如下,设,那么

对上式两边同时除,进一步得到

再把替换掉,最终得到

初始化,这样就可以通过递推法求出模奇素数的所有逆元了。

另外的所有逆元值对应中所有的数,比如,那么对应的逆元是

题目:http://www.lydsy.com/JudgeOnline/problem.php?id=2186

题意:互质的数的个数,其中

分析:因为,所以,我们很容易知道如下结论

   对于两个正整数,如果的倍数,那么中与互素的数的个数为

     本结论是很好证明的,因为中与互素的个数为,又知道,所以

结论成立。那么对于本题,答案就是

其中为小于等于的所有素数,先筛选出来即可。由于最终答案对一个质数取模,所以要用逆元,这里

求逆元就有技巧了,用刚刚介绍的递推法预处理,否则会TLE的。

接下来还有一个关于逆元的有意思的题目,描述如下

证明:

其中

所以只需要证明,而我们知道的逆元对应全部

中的所有数,既是单射也是满射。

所以进一步得到

证明完毕!

C(n+m,m) mod p的一类算法的更多相关文章

  1. MOD 10,11算法(GB/T 17710-1999 数据处理 校验码系统 ),使用javascript实现

    原文链接:http://chunniu.info/p/74.html GB/T 17710-1999 数据处理 校验码系统 ,便于使用,使用javascript做了一个页面 [php] var NUM ...

  2. MOD 10,11算法(GB/T 17710-1999 数据处理 校验码系统 )的 Python实现

    以上是算法简要说明,以下代码为Python实现,不过注意代码中的N=15,不是16. # GB/T 17710 双模校验算法 # QQ 3257132998 def GB_Code(str): str ...

  3. 跨越千年的RSA算法

    转载自http://www.matrix67.com/blog/archives/5100 数论,数学中的皇冠,最纯粹的数学.早在古希腊时代,人们就开始痴迷地研究数字,沉浸于这个几乎没有任何实用价值的 ...

  4. [转载] TLS协议分析 与 现代加密通信协议设计

    https://blog.helong.info/blog/2015/09/06/tls-protocol-analysis-and-crypto-protocol-design/?from=time ...

  5. [转载]RSA算法详解

    原文:http://www.matrix67.com/blog/archives/5100 数论,数学中的皇冠,最纯粹的数学.早在古希腊时代,人们就开始痴迷地研究数字,沉浸于这个几乎没有任何实用价值的 ...

  6. TLS协议分析

    TLS协议分析 本文目标: 学习鉴赏TLS协议的设计,透彻理解原理和重点细节 跟进一下密码学应用领域的历史和进展 整理现代加密通信协议设计的一般思路 本文有门槛,读者需要对现代密码学有清晰而系统的理解 ...

  7. 整数快速乘法/快速幂+矩阵快速幂+Strassen算法

    快速幂算法可以说是ACM一类竞赛中必不可少,并且也是非常基础的一类算法,鉴于我一直学的比较零散,所以今天用这个帖子总结一下 快速乘法通常有两类应用:一.整数的运算,计算(a*b) mod c  二.矩 ...

  8. [模板] BSGS

    BSGS是一种解决一类专门的问题的解法,主要是解决已知A, B, C,求X使得A^x = B (mod p)这一类问题. 解法很简单,先设x = i*m-j(m=ceil(sqrt(p))),然后进行 ...

  9. NOIP 骗分技巧

    目录 第1章 绪论 第2章 从无解出发 \hookrightarrow↪ 2.1 无解情况 \hookrightarrow↪ 2.2 样例——白送的分数 第3章 “艰苦朴素永不忘” \hookrigh ...

随机推荐

  1. PHP基础示例:用正则表达式修改配置信息

    各php工作原理图: 以下是扯代码时间: <?php //配置文件信息 define("HOST","localhost"); //主机名 define( ...

  2. Flink资料(4) -- 类型抽取和序列化

    类型抽取和序列化 本文翻译自Type Extraction and Serialization Flink处理类型的方式比较特殊,包括它自己的类型描述,一般类型抽取和类型序列化框架.该文档描述这些概念 ...

  3. jmeter实例演示

    Jmeter 是比较轻便的性能测试工具,下面根据一个实例演示下jmeter的常见用法 一.前期准备,在使用之前,先分析测试需求,比如:需不要登录?需不需要监视服务器性能?多线程还是多循环?需不需根据流 ...

  4. SublimeText3常用快捷键和优秀插件(转载)

    SublimeText是前端的一个神器,以其精简和可DIY而让广大fans疯狂.好吧不吹了直入正题 -_-!! 首先是安装,如果你有什么软件管家的话搜一下就好,一键安装.然后,有钱的土豪就自己买个吧, ...

  5. Lucky Sum

    Description Lucky Sum time limit per test: 2 seconds memory limit per test: 256 megabytes input: sta ...

  6. 使用verilog实现4选1数据选择器的几种方法

    第一种方法module mux( d1, d2, d3, d4, se1, se2, dout ); input d1; input d2; input d3; input d4; input se1 ...

  7. python 类型转换函数

    python提供了一些可将某个值从一种类型转换为另一种类型的内置函数. 1. int函数可以把任何可以转换为整型的值转换为整型.int可以将浮点数转换为整数,但不会做四舍五入操作,而是直接丢弃小数部分 ...

  8. 4.跟我学solr---SolrRequestHandler具体解释

    概述 我们在使用solr admin在做查询的时候,能够看到Request-Hander(qt)输入栏中有"/select"这样一个uri.当我们点击查询的时候所发起的请求是这种. ...

  9. Lambda表达式图解

    internal delegate int MyDel(int x); public class Lambda { ; };//匿名方法 ; };//Lambda表达式 ; };//Lambda表达式 ...

  10. 动态加载 js

    要实现动态加载JS脚本有4种方法: 1.直接document.write <script language="javascript"> document.write(& ...