【COGS 2965】简单题233

题目描述

有一个 n × m ( 1 n , m 300000 ) 的网格,求从左上角第一个格子走到右下角最后一个格子的方案数(由于方案数过大,请余除23333333),只能选择向下走或向右走。

算法分析

由于只能向左走或向右走,总步数固定为 n + m 2 ,向下走的步数固定为 m 1 ,则答案就是在 n + m 2 步中选择 m 1 步向下走,其余步数向右走的组合数,即 C n + m 2 m 1

由于模数不为质数,我们只能暴力求解,根据组合数公式将阶乘分解质因数后约分,然后使用快速幂计算结果即可,注意数组开要两倍。

代码实现

#include <cstdio>
#include <cstring>
typedef long long int ll;
const int maxn=600005;
const int p=23333333;
inline int mul(int x,int y) {return (long long int)x*y%p;}
int notprime[maxn],prime[maxn],idx=0;
inline void getPrime(int n) {
    memset(notprime,0,sizeof(notprime));idx=0;
    for(int i=2;i<=n;++i) {
        if(!notprime[i]) prime[idx++]=i;
        for(int j=0;j<idx&&(long long int)i*prime[j]<=n;++j) {
            notprime[i*prime[j]]=true;
            if(i%prime[j]==0) break;
        }
    }
}
int times[maxn];
inline void divide(int num,int d) {
    for(int i=0;i<idx;++i) {
        long long int now=prime[i];
        while(now<=num) {
            times[i]+=num/now*d;
            if((long long int)now*prime[i]>num) break;
            else now=now*prime[i];
        }
    }
}
inline int qpow(int n,int k) {
    int ans=1;
    while(k) {
        if(k&1) ans=mul(ans,n);
        n=mul(n,n);k>>=1;
    }
    return ans;
}
inline int C(int n,int k) {
    getPrime(n);
    divide(n,1);divide(n-k,-1);divide(k,-1);
    int ans=1;
    for(int i=0;i<idx;++i) ans=mul(ans,qpow(prime[i],times[i]));
    return ans;
}
int main() {
    freopen("hst.in","r",stdin);freopen("hst.out","w",stdout);
    int n,m;scanf("%d%d",&n,&m);
    printf("%d\n",C(n+m-2,m-1));
    fclose(stdin);fclose(stdout);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/WHZ2018/article/details/81292468