AcWing 1315. 网格

AcWing 1315. 网格

一、题目描述

某城市的街道呈网格状,左下角坐标为 A(0,0),右上角坐标为 B(n,m),其中 nm

现在从 A(0,0)点 出发,只能沿着街道向 正右方 或者 正上方 行走,且不能经过图示中直线左上方的点,即任何途径的点 (x,y) 都要满足 xy,请问在这些前提下,到达 B(n,m) 有多少种走法。

输入格式

仅有一行,包含两个整数 nm,表示城市街区的规模。

输出格式

输出一个整数,表示不同的方案总数。

数据范围
1mn5000

输入样例:

6 6

输出样例:

132

二、解题思路

用求卡特兰数的方法分析一下这个题目就可以得到答案,关于卡特兰数的分析:网址

我们需要求出点 (n,m) 关于y=x+1对称的点的坐标,假设为(a,b),则任何一种不合法的方案都可以转化为到达(a,b)的路径,如下图:

则答案为:Cm+nnCm+na,问题就转变为了如何求解坐标(a,b)

解析几何法

这是高中知识,我们可以列方程求解,根据垂直可以得到一个等式,根据线段中点在对称轴上可以得到另一个等式,可以得到:

{1×bman=1线1y=x+1b+m2=a+n2+1

解方程可得a=m1,b=n+1,因此答案为

Cm+nnCm+nm1

平移对称轴法

  • ① 将直线y=x+1、点(n,m)全部向下移动一个单位,就变成了一条直线y=x和点(n,m1)
  • ② 求(n,m1)关于直线y=x的对称点就是(m1,n)
  • ③ 再将直线和点全部上移回去,就得到(m1,n+1)

因此,答案也是: $$\large \displaystyle C_{m+n}n-C_{m+n}$$

  1. 上面两种方法使用其中一种即可。
  2. 本题需要使用到高精度求解,如果递推的话计算量为100002=1×108,再加上高精度计算会超时,因此这里求解阶乘的方式然后带入公式求组合数,类似于AcWing 888. 求组合数 IV

三、实现代码

#include <bits/stdc++.h>

using namespace std;

const int N = 100010;
int a[N], b[N]; // 两个整数数组保存高精度计算的结果

// 欧拉筛
int primes[N], cnt;
bool st[N];
void get_primes(int n) {
    for (int i = 2; i <= n; i++) {
        if (!st[i]) primes[cnt++] = i;
        for (int j = 0; primes[j] * i <= n; j++) {
            st[primes[j] * i] = true;
            if (i % primes[j] == 0) break;
        }
    }
}

// 计算n!中包含质数p的个数
int get(int n, int p) {
    int s = 0;
    while (n) s += n / p, n /= p;
    return s;
}

// 高精乘低精
void mul(int a[], int &al, int b) {
    int t = 0;
    for (int i = 1; i <= al; i++) {
        t += a[i] * b;
        a[i] = t % 10;
        t /= 10;
    }
    while (t) {
        a[++al] = t % 10;
        t /= 10;
    }
}

// 高精减高精
void sub(int a[], int &al, int b[]) {
    for (int i = 1, t = 0; i <= al; i++) {
        a[i] -= t + b[i];
        if (a[i] < 0)
            a[i] += 10, t = 1;
        else
            t = 0;
    }
    while (al > 1 && !a[al]) al--;
}

// C(a,b)的结果,高精度保存到c数组,同时,返回c数组的长度len
void C(int a, int b, int c[], int &cl) {
    // 高精度的基底,乘法的基数是1
    c[1] = 1;
    cl = 1; // 由于高精度数组中只有一位,是1,所以长度也是1

    for (int i = 0; i < cnt; i++) { // 枚举区间内所有质数
        int p = primes[i];
        /*
        C(a,b)=a!/(b! * (a-b)!)
        a!中有多少个质数因子p
        减去(a-b)!的多少个质数因子p,
        再减去b!的质数因子p的个数,就是总个数
        s记录了p这个质数因子出现的次数
        */
        int s = get(a, p) - get(b, p) - get(a - b, p);
        while (s--) mul(c, cl, p); // 不断的乘p,结果保存到数组c中。len将带回c的有效长度
    }
}

int main() {
    int n, m;
    cin >> n >> m;
    // 筛质数
    get_primes(2 * n);
    int al, bl;
    C(n + m, m, a, al);     // C(n+m,m),将高精度结果记录到a数组中,返回数组有效长度al
    C(n + m, m - 1, b, bl); // C(n+m,m-1),将高精度结果记录到b数组中

    sub(a, al, b); // 计算a-b的高精度减法
    
    // 输出结果,注意是倒序
    for (int i = al; i; i--) printf("%d", a[i]);
    return 0;
}
posted @   糖豆爸爸  阅读(79)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!
历史上的今天:
2019-06-10 在DEV c++ 中如何设置默认的代码模板
2014-06-10 mysql中json数据的拼接方式
Live2D
点击右上角即可分享
微信分享提示