AtCoder Context ABC 125- D - Knight

運行要求
運行時間限制: 2sec
內存限制: 1024MB
原文連接算法

題目
二次元的座標系的原點座標(0,0)上有一名騎士,騎士若是處在格子(i,j)上的話能夠按照以下的方式移動
(i+1,j+2)
(i+2,j+1)
若是騎士想到達(X,Y)的話,有多少種移動方案。數組

輸入前提條件微信

  • 1<=X<=10^6
  • 1<=X<=10^6
  • 輸入的值均爲整數

輸入
輸入都以如下標準從命令行輸入app

X Y

輸出
求騎士從原點(0,0)到(X,Y)的移動方法的總數,結果取10^9+7的餘數spa


例1
輸入命令行

3 3

輸出code

2

(0,0)->(1,2)->(3,3)
(0,0)->(2,1)->(3,3)
兩種方法blog

例2
輸入內存

2 2

輸出get

0

騎士不能移動到(2,2)的座標

例3
輸入

999999 999999

輸出

151840682

最後的結果是和10^9+7相除的餘數


讀懂題目
這題能夠抽象成座標系的題目
Atcoder Context 125D.001.jpeg

解題思路
1.首先有下面兩種走法,紅色的長方形和藍色的長方形

Atcoder Context 125D.002.jpeg
咱們設紅色的長方形的數量是m,藍色的長方形的數量是n。那麼m和n知足如下條件

2*m + n == X
2*n + m == Y

咱們從0開始遍歷m,能夠找到全部知足條件的m和n的組合
遍歷的條件是如下

2*m <= X
m <= Y

另外X+Y = 3m + 3n = 3*(M+N)
說明X+Y必須爲3的倍數,否則的話經過紅色或者藍色長方形是到達不了(X,Y)的

就這樣咱們找到了可能的m,n,題目要求的是全部移動方法的數量。這裏咱們能夠抽象成如下的問題

2.有m個大小同樣的紅球,n個同樣的藍球。咱們任意排列這些球,請問有多少種排法?

這一高中數學排列組合的一道經典題目
答案是C(m+n,n)

證實大概是以下
咱們把全部的排列組合計算一下是(m+n)的階乘
這些組合裏面,由於藍色的球都是同樣的球和紅色的球的也都是同樣的球。
因此
全部的排列組合(m+n)的階乘 = 紅色的球的排列組合(m的階乘)某個數α
全部的排列組合(m+n)的階乘 = 藍色的球的排列組合(n的階乘)乘以某個數β
剩下的(m+n)的階乘/(m的階乘*n的階乘)就是去掉紅球和藍球排列的組合

C(m+n,m) = (m + n)! / (m+n-m)! * m!

Atcoder Context 125D.003.jpeg
3.接下來如何求得答案,這也是很使人頭疼的問題
由於目標點座標X,Y的數值最大可能達到10^6,相應的m,n的值也可能回答道10^5這麼大,咱們貿然的對如此大的數去作階乘顯然會overflow

那麼咱們怎麼辦呢,看看結果公式
(m + n)! / (m+n-m)! * m!
(m+n)的階乘的時候咱們能夠每乘法一次,對10^9+7取餘數,這樣把數值控制在一個很小的範圍之內

可是 /(m+n-m)! * m!這些怎麼辦

咱們根據費馬小定律有以下推斷,如圖
Atcoder Context 125D.004.jpeg

x^mod-2和1/x相互爲對mod取餘的逆元
求1/x對mod的餘數就至關於求x^mod-2對mod的餘數

結果能夠當作((m + n)! (1/1 1/2 1/3 .... 1/(n)) (1/1 1/2 1/3 .... 1/(m)) } % mod
咱們能夠依次求的這些
1/1 % mod
1/2 % mod
1/3 % mod
1/x % mod
可是這樣帶來一個問題,mod=10^9+7很是大的一個數n^(mod-2)顯然也很差計算

有另一種算法
(1/x) % mod = - ((1/(mod % x) % mod) * (mod / x)
咱們設定一個數組叫inverse

inverse[x] = (1/x) % mod
inverse[x] = (-1* inverse[mod % x] * (mod //2 i)) % mod
x=0的時候inverse[0] = 0
x=1的時候inverse[1] = 1
x=2的時候inverse[2] = (-1 * inverse[1] * (mod //2)) % mod
x=3的時候inverse[3] = (-1 * inverse[2] * (mod //3)) % mod
...

代碼

X, Y = map(int,input().split())

def cmb(n, k, mod, fac, ifac):
    k = min(k, n-k)
    return fac[n] * ifac[k] * ifac[n-k] % mod


def make_tables(mod, n):
    fac = [1, 1] # 儲存階乘的數組
    ifac = [1, 1] # 儲存逆元階乘的數組
    inverse = [0, 1] # 儲存逆元的數組

    for i in range(2, n+1):
        fac.append((fac[-1] * i) % mod)
        inverse.append((-inverse[mod % i] * (mod//i)) % mod)
        ifac.append((ifac[-1] * inverse[-1]) % mod)
    return fac, ifac


def comb(m,n):
    MOD = 10**9 + 7
    fac, ifac = make_tables(MOD, m)
    ans = cmb(m, n, MOD, fac, ifac)
    return ans

def calculate(x,y):
    m = 0
    n = 0

    if ((x + y) % 3) != 0:
        print(0)
        return

    result = []

    while (2*m <= x) and (m <= y):
        n = x - 2 * m
        if (2*m + n == x) and (2*n + m == y):
            result.append(comb(m+n,n))

        m = m + 1

    if len(result) == 0:
        print(0)
    else:
        print(min(result))

calculate(X,Y)

總結
這道題有3個難點

  1. 如何求出m,n的全部的組合
  2. 如何抽象出高中數學經典排列組合c(m+n,n)的問題
  3. 在mod數據量比較大的狀況下,如何求得c(m+n,n) % mod

最後本篇文章所提交的代碼裏關於求排列組合的代碼,是網上尋找的。有心的話,能夠收藏,目前代碼競技的職業玩家在排列組合上應該也是使用的這些代碼

※ 另外,我會在個人微信我的訂閱號上推出一些文章,歡迎關注
二維碼.jpg

相關文章
相關標籤/搜索