1. 程式人生 > 實用技巧 >旗木雙翼「逆元」

旗木雙翼「逆元」

旗木雙翼「逆元」

題目描述

菲菲和牛牛在一塊n行m列的棋盤上下棋,菲菲執黑棋先手,牛牛執白棋後手。

棋局開始時,棋盤上沒有任何棋子,兩人輪流在格子上落子,直到填滿棋盤時結束。落子的規則是:一個格子可以落子當且僅當這個格子內沒有棋子且這個格子的左側及上方的所有格子內都有棋子。

_Itachi聽說有不少學弟在省選現場AC了D1T1,解決了菲菲和牛牛的問題,但是_Itachi聽說有的人認為複雜度玄學,_Itachi並不想難為學弟學妹,他想為大家節約時間做剩下的題,所以將簡化版的D1T1帶給大家。

_Itachi也在一塊n行m列的棋盤上下棋,不幸的是_Itachi只有黑棋,不過幸好只有他一個人玩。現在,_Itachi想知道,一共有多少種可能的棋局(不考慮落子順序,只考慮棋子位置)。

_Itachi也不會為難學弟學妹們去寫高精度,所以只需要告訴_Itachi答案mod 998244353(一個質數)的結果。

輸入格式

第一行包括兩個整數n,m表示棋盤為n行m列。

輸出格式

一個整數表示可能的棋局種數。

樣例

樣例輸入1

1 1

樣例輸出1

2

樣例輸入2

2 3

樣例輸出2

10

樣例輸入3

10 10

樣例輸出3

184756

資料範圍與提示

對於 20%的資料n,m<=10

對於 30%的資料n,m<=20

另有 20%的資料n<=5

另有 20%的資料m<=5

對於100%的資料n,m<=100000

思路分析

  • 先打表,發現答案是一個斜著的楊輝三角,不過還有另一種更簡單的方法
  • 這個棋盤放棋子時必須要保證左邊上邊都有棋子,我們可以模擬一條路徑,從左下角到左上角,路徑不同,放的情況就不同,重點是這條路徑只能向上或向下走————即一共走了m+n步,其中有n步向上,情況數即為組合數Cm+nn = (m+n)! / n!m!
  • 因為除法無法取模,所以乘上逆元即可

程式碼

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
typedef long long ll;
const ll mod = 998244353;
ll jc(ll x){ //階乘
    ll ret = 1;
    for(int i = 1;i <= x;i++){
        ret = i*ret%mod;
    }
    return ret;
}
ll quickpow(ll a,ll n,ll p){ //快速冪,費馬小定理求逆元時會用到
    ll ans = 1;
    while(n){
        if(n&1)ans = ans*a%p;
        a = a*a%p;
        n >>= 1;
    }
    return ans;
}
ll ny(ll a,ll p){ //費馬小定理求逆元(保證p是質數)
    return quickpow(a,p-2,p); //a^p-2%p即為a在模p意義下的逆元
}
int main(){
    ll n,m;scanf("%lld%lld",&n,&m);
    int x = (jc(m)*jc(n))%mod;
    ll ans = (jc(m+n)*ny(x,mod))%mod;
    printf("%lld",ans);
    return 0;
}