HEX----组合数+逆元+思维 山东省第八届省赛D题

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
Hint
Author
“浪潮杯”山东省第八届ACM大学生程序设计竞赛(感谢青岛科技大学)
题目链接:http://www.sdutacm.org/onlinejudge2/index.php/Home/Contest/contestproblem/cid/2081/pid/3896


昨晚补了这个题,发现比赛的时候johsnows给我讲的是他的思路,不是题意,我以为那就是题意,赛后发现在赛时想了一个小时的题竟然还没懂题意。。。


题目的意思是说给你一个类似于蜂巢型的东西,每一次只能向左下,中间和右下走一步,给定你(x,y),问你走到(x,y)这个为位置有多少种情况。


我们一步步的来推,我们先定义(2,1)为第二行第一列,(3,2)为第三行第三列,现在我们假定给定的点是(5,2),那么这个位置相对于(1,1)来说向左偏移了两个单位,向下偏移了四个单位。现在我们有如下种走法:

右 中

3 0

2 1

第一组示例代表着是左下走四步,右下走一步,第二组示例是说左下走两步,中间走一步。

拿第一组示例来说,我们会发现,一共走了四步,只要是往左下走三步,往右下走一步即可,并没有顺序关系,所以我们会得到第一种情况的走法C(3,4)*C(1,1)*C(0,0),同理,第二种情况就是C(2,3)*C(0,1)*C(1,1),所以总的走法就是两种情况的加和,所以我们枚举左下,中间,右下的其中一种,另外两个方向的步数就可以被计算出来,所以就出来了。

小提示:中间是直接往下移,所以中间可以算是先左下,后右下,所以从中间走一步顶两步。

上面的是wmn的做法,我本想这样敲,发现pbh有更简单的做法,思路差不多,就是一个正推,一个反推。

我们先计算出左右偏移量,然后我们会发现从(1,1)走到(x,y)的总方案数是A(a+b+c,a+b+c)(a代表左偏移量,b代表右偏移量,c代表从中间走的步数,我们只要走到这些步数就可以,然后计算顺序有多少种就可以了),然后我们会发现,走左边会有重复,中间和右边也是,所以我们减掉重复的步数A(a,a),A(b,b),A(c,c),所以最后结果就出来了,这个比较好敲,但是个逆向思维,算出总的,减掉重复的(怎么有种容斥的感觉)。

算左偏移量有个小技巧,假如输入(x,y),那么走的步数最多是x-1步((1,1)不用走),初始化的时候也就是说a+b==x-1,很巧妙的是左偏移量就是x-y,右偏移量就是x-1-a,中间就是0,仔细想想很容易就懂了。

记得把除法取膜换成乘逆元取膜,记得预处理。

代码:

#include <cstdio>
#include <cstring>
#include <iostream>
#define LL long long
using namespace std;
const int mod=1000000007;
LL aa[200000];
LL bb[200000];
LL inv(LL t,LL p){
    return t==1?1:(p-p/t)*inv(p%t,p)%p;
}
void Init(){
    aa[0]=1;
    bb[0]=1;
    for(int i=1;i<=100000;i++){
        aa[i]=(aa[i-1]*i)%mod;//(A(n,n))
        bb[i]=inv(aa[i]%mod,mod);//(A(n,n)的逆元)
    }
}
int main(){
    int n,m;
    Init();
    while(~scanf("%d%d",&n,&m)){
        int a=n-m,b=n-1-a;
        int c=0;
        int ans=0;
        while(~a&&~b){
            ans=(ans+(((aa[a+b+c]*bb[a]+mod)%mod*bb[b]+mod)%mod*bb[c]+mod)%mod)%mod;
            a--,b--,c++;
        }
        printf("%d\n",ans);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值