题解:洛谷 P2822 [NOIP 2016 提高组] 组合数问题

【题目来源】

洛谷:[P2822 NOIP 2016 提高组] 组合数问题 - 洛谷

【题目描述】

组合数 \((_m^n)\) 表示的是从 \(n\) 个物品中选出 \(m\) 个物品的方案数。举个例子,从 \((1,2,3)\) 三个物品中选择两个物品可以有 \((1,2),(1,3),(2,3)\) 这三种选择方法。根据组合数的定义,我们可以给出计算组合数 \((_m^n)\) 的一般公式:

\((_m^n)=\displaystyle\frac{n!}{m!(n-m)!}\) 其中 \(n!=1×2×⋯×n\);特别地,定义 \(0!=1\)

小葱想知道如果给定 \(n,m\)\(k\),对于所有的 \(0≤i≤n,0≤j≤min(i,m)\) 有多少对 \((i,j)\) 满足 \(k|(_j^i)\)

【输入】

第一行有两个整数 \(t,k\),其中 \(t\) 代表该测试点总共有多少组测试数据,\(k\) 的意义见问题描述。

接下来 \(t\) 行每行两个整数 \(n,m\),其中 \(n,m\) 的意义见问题描述。

【输出】

\(t\) 行,每行一个整数代表所有的 \(0≤i≤n,0≤j≤min(i,m)\) 中有多少对 \((i,j)\) 满足 \(k|(_j^i)\)

【输入样例】

1 2
3 3

【输出样例】

1

【算法标签】

《洛谷 P2822 组合数问题》 #数学# #组合数学# #前缀和# #NOIP提高组# #2016#

【代码详解】

#include <bits/stdc++.h>
using namespace std;

int t;                  // 测试用例数量
int k;                  // 模数
int a[2005][2005];      // 存储组合数模k的结果
int f[2005][2005];      // 前缀和数组,记录满足条件的组合数数量
int n, m;               // 每次查询的参数

int main()
{
    // 输入测试用例数量和模数
    cin >> t >> k;

    // 初始化组合数数组(杨辉三角)
    a[1][1] = 1;
    for (int i = 1; i <= 2000; i++)
    {
        for (int j = 1; j <= i; j++)
        {
            // 边界条件:第一列和最后一列都为1
            if (j == 1 || j == i)
            {
                a[i][j] = 1;
            }
            else
            {
                // 递推计算组合数并取模
                a[i][j] = (a[i - 1][j - 1] + a[i - 1][j]) % k;
            }
        }
    }

    // 初始化前缀和数组的第一列
    for (int i = 1; i <= 2000; i++)
    {
        f[i][1] = (a[i][1] == 0);
    }

    // 计算前缀和数组
    for (int i = 1; i <= 2000; i++)
    {
        for (int j = 2; j <= i; j++)
        {
            // 分情况计算前缀和
            if (i - 1 < j)
            {
                f[i][j] = f[i - 1][i - 1] + f[i][j - 1] - f[i - 1][j - 1];
            }
            else
            {
                f[i][j] = f[i - 1][j] + f[i][j - 1] - f[i - 1][j - 1];
            }

            // 如果当前组合数模k等于0,则计数加1
            f[i][j] += (a[i][j] == 0);
        }
    }

    // 处理每个查询
    while (t--)
    {
        cin >> n >> m;
        // 确保m不超过n
        m = min(n, m);
        // 输出结果(注意索引偏移)
        cout << f[n + 1][m + 1] << endl;
    }

    return 0;
}

【运行结果】

1 2
3 3
1
posted @ 2026-02-18 20:06  团爸讲算法  阅读(25)  评论(0)    收藏  举报