旗木双翼「逆元」

旗木双翼「逆元」

题目描述

菲菲和牛牛在一块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;
}
posted @ 2020-07-15 20:10  HH_Halo  阅读(130)  评论(0编辑  收藏  举报