百度之星 初赛2 瞬间转移 [杨辉三角]

来源:互联网 发布:淘宝能不能换身份证 编辑:程序博客网 时间:2024/05/16 05:22

传送阵: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取模。

http://acm.hdu.edu.cn/data/images/C702-1003-1.jpg

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

————————————————————-

#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

#include <iostream>#include <cstdio>#include <algorithm>#include <cmath>#include <cstring>using namespace std;#define lld  __int64lld  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;}
0 0