[原创]百度之星 初赛2 瞬间转移 [杨辉三角]
2016-05-22 18:08:16 Tabris_ 阅读数:543
博客爬取于2020-06-14 22:44:26
以下为正文
版权声明:本文为Tabris原创文章,未经博主允许不得私自转载。https://blog.csdn.net/qq_33184171/article/details/51476184
传送阵:http://acm.hdu.edu.cn/showproblem.php?pid=5698
瞬间移动
Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others) Total Submission(s): 128 Accepted Submission(s): 61
Problem Description 有一个无限大的矩形,初始时你在左上角(即第一行第一列),每次你都可以选择一个右下方格子,并瞬移过去(如从下图中的红色格子能直接瞬移到蓝色格子),求到第n行第m列的格子有几种方案,答案对1000000007取模。
Input 多组测试数据。
两个整数n,m(2≤n,m≤100000)
Output 一个整数表示答案
Sample Input 4 5
Sample Output 10
Source 2016"百度之星" - 初赛(Astar Round2B)
其实写了写就是一个杨辉三角,只不过行变换了一下
杨辉三角是一个由数字排列成的三角形数表,一般形式如下: 1 n=0 1 1 n=1 1 2 1 n=2 1 3 3 1 n=3 1 4 6 4 1 n=4 1 5 10 10 5 1 n=5 1 6 15 20 15 6 1 n=6 …… 特征 与二项式定理的关系:杨辉三角的第n行就是二项式 展开式的系数列. 对称性:杨辉三角中的数字左、右对称,对称轴是杨辉三角形底边上的“高”. 结构特征:杨辉三角除斜边上1以外的各数,都等于它“肩上”的两数之和. 这些数排列的形状像等腰三角形,两腰上的数都是1. 从右往左斜着看,从左往右斜着看,和前面的看法一样,这个数列是左右对称的. 上面两个数之和就是下面的一行的数. 这行数是第几行,就是第二个数加一.
而在本题中 是这样的、 1 1 1 1 1 1 1 1 2 3 4 5 6 1 3 6 10 15 1 4 10 20 1 5 15 1 6 1
看了看其实就是行变了 列并没有变 把它改一下 行变成行加列-1 即可;
最后的最后直接贴上求组合数的模板AC…
附本题AC 代码
方案1 187ms
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 # include <iostream> # include <string.h> # include <algorithm> # include <math.h> # include <vector> # include <stdio.h> using namespace std; const int MOD = 1e9+7; const int MAXN = 1000000; bool arr[MAXN+100] = {false}; vector<int> produce_prim_number() { vector<int> prim; prim.push_back(2); int i,j; for(i=3; i*i<=MAXN; i+=2) { if(!arr[i]) { prim.push_back(i); for(j=i*i; j<=MAXN; j+=i) arr[j] = true; } } while(i<=MAXN) { if(!arr[i]) prim.push_back(i); i+=2; } return prim; } //计算n!中素因子p的指数 int Cal(int x, int p) { int ans = 0; long long rec = p; while(x>=rec) { ans += x/rec; rec *= p; } return ans; } //计算n的k次方对M取模,二分法 int Pow(long long n, int k, int MOD) { long long ans = 1; while(k) { if(k&1) { ans = (ans * n) % MOD; } n = (n * n) % MOD; k >>= 1; } return ans; } //计算C(n,m) int Combination(int n, int m) { vector<int> prim = produce_prim_number(); long long ans = 1; int num; for(int i=0; i<prim.size() && prim[i]<=n; ++i) { num = Cal(n, prim[i]) - Cal(m, prim[i]) - Cal(n-m, prim[i]); ans = (ans * Pow(prim[i], num, MOD)) % MOD; } return ans; } int main() { int n,m; while(~scanf("%d%d",&n,&m)) { n=m+n-2; printf("%d\n",Combination(n-2,m-2)); } return 0; }
方案 2 31ms
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 # include <iostream> # include <cstdio> # include <algorithm> # include <cmath> # include <cstring> using namespace std; # define lld __int64 lld n, m, p; lld Ext_gcd(lld a,lld b,lld &x,lld &y) { if(b==0) { x=1, y=0; return a; } lld ret= Ext_gcd(b,a%b,y,x); y-= a/b*x; return ret; } lld Inv(lld a,int m) ///求逆元 { lld d,x,y,t= (lld)m; d= Ext_gcd(a,t,x,y); if(d==1) return (x%t+t)%t; return -1; } lld Cm(lld n, lld m, lld p) ///组合数学 { lld a=1, b=1; if(m>n) return 0; while(m) { a=(a*n)%p; b=(b*m)%p; m--; n--; } return (lld)a*Inv(b,p)%p; ///(a/b)%p 等价于 a*(b,p)的逆元 } int Lucas(lld n, lld m, lld p) ///把n分段递归求解相乘 { if(m==0) return 1; return (lld)Cm(n%p,m%p,p)*(lld)Lucas(n/p,m/p,p)%p; } int main() { int T; while(~scanf("%I64d%I64d",&n,&m)) { n--,m--; m--; __int64 p=1000000007; printf("%d\n",Lucas(n+m-1,m,p)); } return 0; }