河工大第一屆校賽 D.公園遊玩(組合數計算)
阿新 • • 發佈:2021-06-11
- 題目:公園遊玩
- 題意:依次經過k個點,求出其最短總路徑的方案數.
- 題解:點A(x1,y1)到點B(x2, y2)的最短距離方案數為(高中組合知識:距離為n,向下走m次到達目的地最短距離的方案數):
n = x2 - x1 + y2 - y1
m = x2 - x1
C(n, m) = n! / (m! * (n - m)!)
- 乘法逆元知識點
- 因為此題求的模為質數,所以可用預處理方式處理階乘與階乘逆元(最後要求餘數),式子轉換成:
C(n, m) = n! * infact(m) * infact(n-m)
- 最後答案取模需要注意:a * b % mod = (a % mod * b % mod) % mod,若a < mod & b < mod可以不用化簡,可以直接使用 a * b % mod,因為兩者效果一致,可以發現a < mod 此時a % mod = a,所以沒必要...接著依次累推下去,若a * b * c % mod, 可以把a * b看成一個整體,先做一個取模a * b % mod,並令為x,按照上面的步驟依次累推,可以發現只要每兩個數相乘取一次模即可.
- 程式碼:
#include<iostream> #include<cstdio> using namespace std; typedef long long ll; const int mod = 1e9 + 7; const int N = 2e5 + 5; ll n, m, k, ans = 1; ll fact[N], infact[N]; //階乘求餘後的結果,階乘的逆元求餘後的結果 struct Node { ll x, y; }g[N]; ll qpow(ll a, ll x, ll p) { ll res = 1; while(x) { if(x & 1) res = res * a % p; a = a * a % p; x >>= 1; } return res; } int main() { cin >> n >> m >> k; g[0].x = 1, g[0].y = 1; for(ll i = 1; i <= k; i++) cin >> g[i].x >> g[i].y; ++ k; g[k].x = n, g[k].y = m; //預處理階乘的餘數與階乘逆元的餘數 fact[0] = 1, infact[0] = 1; for(ll i = 1; i <= N - 5; i++) { fact[i] = i * fact[i-1] % mod; infact[i] = qpow(fact[i], mod - 2, mod); } for(ll i = 1; i <= k; i++) { ll dx = abs(g[i].x - g[i-1].x); ll dy = abs(g[i].y - g[i-1].y); ll sum = dx + dy; ans = ans * fact[sum] % mod * infact[dx] % mod * infact[sum - dx] % mod; //C(sum, dx) = (sum! / dx! * ( (sum - dx)!) ) } cout << ans << endl; return 0; }