HEX SDUT 3896 17年山东省赛D题

探讨在六边形网格中从起点到指定位置的所有可能路径数量,介绍了解决问题的两种方法及其背后的数学原理,并提供了一个使用逆元进行模运算的C++实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目

HEX

Time Limit: 4000MS  Memory Limit: 131072KB
Problem Description

On a plain of hexagonal grid, we define a step as one move from the current grid to the lower/lower-left/lower-right grid. For example, we can move from (1,1) to (2,1), (2,2) or (3,2).

In the following graph we give a demonstrate of how this coordinate system works.

Your task is to calculate how many possible ways can you get to grid(A,B) from gird(1,1), where A and B represent the grid is on the B-th position of the A-th line.

Input

For each test case, two integers A (1<=A<=100000) and B (1<=B<=A) are given in a line, process till the end of file, the number of test cases is around 1200.

Output

For each case output one integer in a line, the number of ways to get to the destination MOD 1000000007.

Example Input
1 1
3 2
100000 100000
Example Output
1
3
1
    题目大意


给了我们一个类似杨辉三角形状的东西,告诉我们其中的方格可以移动,他有三种移动方式:左下,右下,垂直向下,现在他要我们求给定一个坐标点(A,B),我们从(1,1)到(A,B)有多少种方式?


    解题思路

我们通过观察可以看得出来从(1,1)到(A,B)有两种主要的方式

方式一:走的方式中不包括垂直向下

方式二:走的方式中有垂直向下

这里用一个例子来展开

我们假设所求为(1,1)到(5,3)的路径数


我们可以看到,

方式一:需要走两次左下和两次右下。

方式二:①一次左下一次右下,一次垂直向下   ②两次垂直向下

  通过比较方式一和方式二我们发现了一件有趣的事,一次垂直向下操作相当于一次左下加一次右下操作

这里我们先来讨论方式一,方式一的路径数是

 

l是左下的次数,r是右下的次数,A代表全排列也就是阶乘

为什么要采用这个公式呢?这个可以看成是一个排列组合的问题,先把所有的步骤全排列然后去掉其中重复的组合数(高中知识,如果还没看懂,可以问一下同学或者百度)


方式二的求法只要枚举就可以了,枚举方式是,每次left--,right--,down++,这个时候的公式就变成了


枚举到right或者left其中一个为0,这就代表没有办法再抵消成一个down了

因为本题要求我们mod1e9+7,所以就想到了逆元

逆元不会的话自行学习吧,这里主要讲解这一题的思路

#include<iostream>
#include<cstdio>
using namespace std;
#define LL long long
const int fcnt=200005;
const LL mod=1000000000+7;
LL fac[fcnt];
LL inv[fcnt];
void getfac()
{
    fac[0]=1;
    for(int i=1;i<fcnt;i++)
        fac[i]=fac[i-1]*i%mod;
}
LL quickpow(LL a,LL b)
{
    if(b<0) return 0;
    LL ret=1;
    a%=mod;
    while(b)
    {
        if(b&1)
            ret=(ret*a)%mod;
        b>>=1;
        a=(a*a)%mod;
    }
    return ret;
}
void getinv()
{
    inv[0]=1;
    for(int i=1;i<fcnt;i++)
        inv[i]=quickpow(fac[i],mod-2);
}
int main()
{
  LL a,b;
  getfac();
  getinv();
  while(scanf("%lld%lld",&a,&b)!=EOF)
  {
      LL right=b-1;
      LL left=a-b;
      LL down=0;
      LL sum=0;
      while(right>=0&&left>=0)
      {
          sum=(sum+(((fac[right+left+down]*inv[right])%mod*inv[left])%mod*inv[down])%mod)%mod;
          right--;left--;down++;
      }
      printf("%lld\n",sum);
  }
  return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值