【计蒜客题解/洛谷题解/NOIP2016提高组】T2034/P2822 组合数问题

本题目的题解写得较长,照顾一些初中同学(虽然我自己也是初中生),同时也方便作者以后复习使用。如有错误或疑问,请尽快私信作者

题目概况

洛谷链接: https://www.luogu.com.cn/problem/P2822
计蒜客链接: https://nanti.jisuanke.com/t/T2034
**难度:**普及+/提高(计蒜客评级普及T3 ,是不是低了

题目分析

简化题目: 定义一个数组求出C[n][min(n, m)]中满足C(m, n)k的倍数的有多少
涉及知识点: 前缀和以及杨辉三角,组合数的相关公式等数学预备知识
解题思路:
在讲思路之前,我先简单说一下预备知识供大家参考:

一、组合数

在这里插入图片描述
数据范围较大,阶乘计算不现实,在这里给出一个关于组合数的递推公式(本题只涉及 例1,例 2各位看官有兴趣可以了解一下)
对,就是高中数学选修二里的那玩意(翻教材去咯~)
在这里插入图片描述
我们也可以把该式变形为:C(m, n) = C(m, n - 1) + C(m - 1,n - 1)
所以用代码实现,递推式即为c[i][j] = c[i - 1][j] + c[i - 1][j - 1]

二、杨辉三角在这里插入图片描述

形如此图即为杨辉三角,应该都听过,递推式即为dp[i][j] = dp[i - 1][j] + dp[i - 1][j - 1]

三、解题思路概括

首先我们发现组合数递推公式和杨辉三角递推公式相同,也就是说如果抽象的组合数我们无法想明白,就把它理解为杨辉三角做一个前缀和的预处理,但要注意,在最朴素的杨辉三角中,最右侧的元素无法更新,我们需要通过继承的方式从上到上更新。这个组合数我们可以在过程中直接模k,如果模k等于0,就把数量在前缀和中加1。

代码要点拆解

一、预处理环节

直接看注释
二维前缀和递推公式:
在这里插入图片描述
注意区分 :区域是前缀和的区域; 子矩阵指的是原矩阵中的区域
令x2 = n, y2 = m,假设A区域等于A子矩阵(视同)就是(1,1)。我们要输出的是D区域的,但是:

                  D = C区域(A,C子矩阵) + B区域(A,B子矩阵)                

所以在加上B,C区域后,我们需要把A区域再减一遍,去重。

    //预处理
    c[0][0] = c[1][0] = c[1][1] = 1; //三角上第0行和第1行需要预定义(类比组合数)
    for (int i =