[CodeVs1515]跳(lucas定理+费马小定理)

      嘿嘿嘿好久没写数学题了,偶尔看到一道写一写。。。 

      题目大意:一个(n+1)*(m+1)【0<=n, m<=10^12,n*m<=10^12】的矩阵,C(0,0)=1,C(x,y)=C(x-1,y)+C(x,y-1),求从0,0走到n,m路上最小权值(即为前面的C)和mod 10^9+7。

      看到这个C(x,y)=C(x-1,y)+C(x,y-1),第一反应就是杨辉三角,所以这个矩阵其实就是一个由组合数组成的矩阵,第i行第j列的权值为C(i+j,j)【注意这个矩形起点是(0,0)】。

      我们可以发现(0,0)~(n,0)和(0,0)~(0,m)上都是1,所以我们肯定选择走这条路,而且要走长的那条,不过我们只走(0,0)~(n-1,0)或(0,0)~(0,m-1),因为(n,0)或(0,m)接下来要算到,反正没有太大影响,那么这一段的权值和为max(n,m)。接下来就是剩下的那一段了,显然往上和往左走肯定是亏的,所以我们继续往终点走,而这一段的路径则为C(max(n,m)+i,i)【0≤i≤min(n,m)】的和。看不懂的话看看样例,如下图:                       

      上图的1和3就是C(2+0,0)和C(2+1,1)的值,其实只要把矩阵顺时针旋转45°就是杨辉三角了,而C(max(n,m)+i,i)【0≤i≤min(n,m)】的和就是C(n+m+1,min(n,m)),证明略。所以这个矩阵的最短路径则为max(n,m)+C(n+m+1,min(n,m))。这题数据范围是比较猎奇的,但是毋庸置疑的是暴力求组合数取模肯定是不行的啊,这里我们就要用到lucas定理了,lucas定理即C(a,b)mod p=C(a/p,b/p)*C(a mod p,b mod p),然后就用费马小定理+乘法逆元求一下组合数就行辣,然后这题就写完了,代码很短也很容易理解。

const
  p=1000000007;
var
  n,m,t:int64;

function qp(a,b:int64):int64;//快速幂
var
  y,t:int64;
begin
  y:=a mod p;t:=1;
  while b>0 do
  begin
    if b and 1=1 then t:=t*y mod p;
    y:=y*y mod p;
    b:=b>>1;
  end;
  exit(t mod p);
end;

function C(a,b:int64):int64;//费马小定理+乘法逆元
var
  i:longint;
  aa,bb:int64;
begin
  if b>a then exit(0);
  if b>a-b then b:=a-b;
  aa:=1;bb:=1;
  for i:=1 to b do
  begin
    aa:=aa*(a-i+1) mod p;
    bb:=bb*i mod p;
  end;
  exit(aa*qp(bb,p-2) mod p);
end;

function lucas(a,b:int64):int64;//lucas定理
begin
  exit(C(a div p,b div p)*C(a mod p,b mod p)mod p);
end;

begin
  readln(n,m);
  if n>m then
  begin
    t:=n;n:=m;m:=t;
  end;
  writeln((m+lucas(n+m+1,n))mod p);
end.
View Code
原文地址:https://www.cnblogs.com/Sakits/p/5352601.html