• CodeVs1515 跳


    题目描述 Description

    邪教喜欢在各种各样空间内跳。

    现在,邪教来到了一个二维平面。在这个平面内,如果邪教当前跳到了(x,y),那么他下一步可以选择跳到以下4个点:(x-1,y), (x+1,y), (x,y-1), (x,y+1)。

    而每当邪教到达一个点,他需要耗费一些体力,假设到达(x,y)需要耗费的体力用C(x,y)表示。

    对于C(x,y),有以下几个性质:

    1、若x=0或者y=0,则C(x,y)=1。

    2、若x>0且y>0,则C(x,y)=C(x,y-1)+C(x-1,y)。

    3、若x<0且y<0,则C(x,y)=无穷大。

    现在,邪教想知道从(0,0)出发到(N,M),最少花费多少体力(到达(0,0)点花费的体力也需要被算入)。

    由于答案可能很大,只需要输出答案对10^9+7取模的结果。

    输入描述 Input Description

    读入两个整数N,M,表示邪教想到达的点。

    输出描述 Output Description

    输出仅一个整数,表示邪教需要花费的最小体力对10^9+7取模的结果。

    样例输入 Sample Input

    1 2

    样例输出 Sample Output

    6

    数据范围及提示 Data Size & Hint

    对于10%的数据,满足N, M<=20;

    对于30%的数据,满足N, M<=100;

    对于60%的数据,满足min(N,M)<=100;

    对于100%的数据,满足0<=N, M<=10^12,N*M<=10^12。

    数学问题 组合数 lucas定理

    看到那个C的表达式就觉得和组合数有关系,于是欢快地打了个小表,发现——和组合数没多大关系

    以左上角为顶点,每个位置的值以杨辉三角形式增加。根据这一性质可以想出一个贪心方法:只走直线,先贴着边走到目标点所在的行/列,然后直走过去。

    于是ans=max(n,m)+ Σ C(n+m,d)  (1<=d<=min(n,m))  ← C是组合数

    题解说后面那个ΣC 就等于C(n+m+1,min(n,m))

    看到别的题解写了lucas定理,自己想了想觉得可以暴力推过去。N*M<=10^12,根据C(n,m)=c(n,n-m)的性质可知公式求组合数最多循环10^6次,可以接受。

    确实可以推过去,然而WA了几个点,原因是这样算,乘数爆longlong了。

    又加了个快速乘就过了。

    下附lucas定理写法:

     1 /*by SilverN*/
     2 #include<algorithm>
     3 #include<iostream>
     4 #include<cstring>
     5 #include<cstdio>
     6 #include<cmath>
     7 #include<vector>
     8 #define LL long long
     9 using namespace std;
    10 const int mod=1e9+7;
    11 LL ksmul(LL a,LL b){
    12     a%=mod;b%=mod;LL res=0;
    13     while(b){
    14         if(b&1){res+=a;if(res>mod)res-=mod;}
    15         a=(a<<1)%mod;
    16         b>>=1;
    17     }
    18     return res;
    19 }
    20 LL ksm(LL a,LL k){
    21     LL res=1;
    22     while(k){
    23         if(k&1)res=ksmul(res,a);
    24         a=ksmul(a,a);
    25         k>>=1;
    26     }
    27     return res;
    28 }
    29 LL solve(LL n,LL m){
    30     m=max(m,n-m);
    31     LL res=1,inv=1;
    32     for(LL i=m+1;i<=n;i++){
    33         res=ksmul(res,i);
    34         inv=ksmul(inv,i-m);
    35 //        printf("res:%lld inv:%lld
    ",res,inv);
    36     }
    37     inv=ksm(inv,mod-2)%mod;
    38     res=ksmul(res,inv);
    39     return res;
    40 }
    41 LL n,m,ans=0;
    42 int main(){
    43     int i,j;
    44     scanf("%lld%lld",&n,&m);
    45     if(n<m)swap(n,m);
    46     ans+=n%mod;
    47     (ans+=solve(n+m+1,m))%=mod;
    48     printf("%lld
    ",ans);
    49     return 0;
    50 }

    lucas还是快一点

     1 /*by SilverN*/
     2 #include<algorithm>
     3 #include<iostream>
     4 #include<cstring>
     5 #include<cstdio>
     6 #include<cmath>
     7 #include<vector>
     8 #define LL long long
     9 using namespace std;
    10 const int mod=1e9+7;
    11 LL ksm(LL a,LL k){
    12     LL res=1;
    13     while(k){
    14         if(k&1)res=(res*a)%mod;
    15         a=(a*a)%mod;
    16         k>>=1;
    17     }
    18     return res;
    19 }
    20 LL clc(LL n,LL m){
    21     if(n<m)return 0;
    22     m=min(m,n-m);
    23     LL res=1,inv=1;
    24     for(LL i=1;i<=m;i++){
    25         res=res*(n-i+1)%mod;
    26         inv=inv*i%mod;
    27     }
    28     res=res*(ksm(inv,mod-2))%mod;
    29     return res;
    30 }
    31 LL lucas(LL a,LL b){
    32     if(!b)return 1;
    33     return clc(a%mod,b%mod)*(lucas(a/mod,b/mod)%mod)%mod;
    34 }
    35 LL n,m,ans=0;
    36 int main(){
    37     int i,j;
    38     scanf("%lld%lld",&n,&m);
    39     if(n<m)swap(n,m);
    40     ans+=n%mod;
    41     (ans+=lucas(n+m+1,m))%=mod;
    42     printf("%lld
    ",ans);
    43     return 0;
    44 }
    lucas
  • 相关阅读:
    08月24日总结
    08月23日总结
    08月22日总结
    装饰器
    卢菲菲最强大脑记忆训练法全套教程 01
    LeetCode 704 二分查找
    LeetCode 1480 一维数组的动态和
    NIO 总结
    LeetCode 881 救生艇
    url的组成部分
  • 原文地址:https://www.cnblogs.com/SilverNebula/p/6543221.html
Copyright © 2020-2023  润新知