Luogu P2822 组合数问题(前缀和)

Posted coder-uranus

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Luogu P2822 组合数问题(前缀和)相关的知识,希望对你有一定的参考价值。

P2822 组合数问题

题意

题目描述

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

[C_n^m=frac{n!}{m!(n-m)!}]

其中(n!=1 imes 2 imes cdots imes n);特别地,定义(0!=1)

小葱想知道如果给定(n,m)(k),对于所有的(0leq ileq n,0leq jleq min left( i, m ight))有多少对((i,j))满足(C_i^j)(k)的倍数。

输入输出格式

输入格式:

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

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

输出格式:

(t)行,每行一个整数代表所有的(0leq ileq n,0leq jleq min left( i,m ight))中有多少对((i,j))满足(C_i^j)(k)的倍数。

输入输出样例

输入样例#1:

1 2
3 3

输出样例#1:

1

输入样例#2:

2 5
4 5
6 7

输出样例#2:

0
7

说明

【样例1说明】

在所有可能的情况中,只有(C_2^1=2)(2)的倍数。

【子任务】

技术分享图片

思路

(10)个月以前,当我和一位数竞党聊起这道题的时候,他启发我,可以利用(k)的特性来特判每一个数据点。当时的我嫌麻烦,没有这样写。如今问了(Mercury)这道题的做法,才发现正解才是(OI)思维,之前的想法太偏数学了。

首先,杨辉三角的值与组合数相同,我们可以用求杨辉三角的方法很快求出组合数。在求的过程中,组合数对(k)取模,若该位为(0),则说明它是(k)的倍数。

然后就是这道题的精髓了:用一个二维数组(s[i][j])统计组合数为(0)的情况的前缀和。转移方法是:(s[i][j]=s[i-1][j]+s[i][j-1]-s[i-1][j-1]+[c[i][j]=0])

然后直接输出前缀和就好啦。

AC代码

#include<bits/stdc++.h>
using namespace std;
int t,k,a[2005][2005],s[2005][2005];
int read()
{
    int re=0;char ch=getchar();
    while(!isdigit(ch)) ch=getchar();
    while(isdigit(ch)) re=(re<<3)+(re<<1)+ch-'0',ch=getchar();
    return re;
}
int main()
{
    t=read(),k=read();
    a[1][1]=1;
    for(int i=2;i<=2001;i++)
    {
        for(int j=1;j<=i;j++) a[i][j]=(a[i-1][j-1]+a[i-1][j])%k;
        for(int j=1;j<=i;j++) s[i][j]=s[i][j-1]+(!a[i][j]);
        for(int j=i+1;j<=2001;j++) s[i][j]=s[i][i];
        for(int j=1;j<=2001;j++) s[i][j]+=s[i-1][j];
    }
    while(t--)
    {
        int x=read(),y=read();
        printf("%d
",s[x+1][y+1]);
    }
    return 0;
}

以上是关于Luogu P2822 组合数问题(前缀和)的主要内容,如果未能解决你的问题,请参考以下文章

基础数学问题 P2822 组合数问题前缀和

做题记录:P2822 组合数问题

洛谷——P2822 组合数问题

LuoguP2822 组合数问题(组合数,二维前缀和)

[题解](组合数/矩阵的前缀和)luogu_P2822组合数问题

P2822 组合数问题