嘿嘿嘿好久没写数学题了,偶尔看到一道写一写。。。
题目大意:一个(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(n+m+i-1,i)【0≤i≤min(n,m)】的和。看不懂的话看看样例,如下图:
上图的1和3就是C(1+2+0-1,0)和C(1+2+1-1,1)的值,其实只要把矩阵顺时针旋转45°就是杨辉三角了,而C(n+m+i-1,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.