NOIP2016组合数问题(洛谷2822)

来源:互联网 发布:mac中如何切换输入法 编辑:程序博客网 时间:2024/05/21 03:28

标签:数论

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

其中n! = 1×2×···×n
小葱想知道如果给定n,m和k,对于所有的0<=i<= n,0<=j<= min(i,m)有多少对 (i,j)满足是k的倍数。
【输入格式】
第一行有两个整数t,k,其中t代表该测试点总共有多少组测试数据,k的意义见【问题描述】。
接下来t行每行两个整数n,m,其中n,m的意义见【问题描述】。
【输出格式】
t行,每行一个整数代表答案。
【样例输入1】
1 2
3 3
【样例输出1】
1
【样例输入2】
2 5
4 5
6 7
【样例输出2】
0
7
【数据范围】

分析:首先我们要知道一个关于组合数的递推公式:C(i,j)=C(i-1,j)+C(i-1,j-1),其实就是杨辉三角

然而这题并不需要我们求每一个组合数C(i,j)的值,只要知道C(i,j)对于k的余数即可

先预处理每一个C (i,j)对于k的余数,存在f数组内,之后将f数组转化为num数组,表示当前i,j项中C(i,j)被k整除的个数

在线读入每组测试数据n,m,直接加上每个num[i][n]值,输出即可

 

参考代码

#include<bits/stdc++.h>using namespace std;inline int read(){    int f=1,x=0;char ch=getchar();    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}    return x*f;}int f[2006][2006],sum[2006][2006];int main(){    int t=read(),k=read();    for(int i=0;i<=2005;i++)    {        f[i][1]=i%k;        f[i][i]=1;    }    for(int i=2;i<=2005;i++)        for(int j=2;j<i;j++)            f[i][j]=(f[i-1][j]+f[i-1][j-1])%k;    for(int i=1;i<=2005;i++)        for(int j=1;j<=i;j++)            if(f[i][j]==0)sum[i][j]=sum[i][j-1]+1;            else sum[i][j]=sum[i][j-1];    for(int i=1;i<=t;i++)    {        int x=read(),y=read(),ans=0;        for(int j=1;j<=x;j++)            if(j>y)ans+=sum[j][y];            else ans+=sum[j][j];        cout<<ans<<endl;    }    return 0;}



 

原创粉丝点击