Noip2016组合数问题(洛谷2822)

阅读全文大概需要 1分钟
本文总阅读量
Posted by yjjr's blog on February 6, 2018

标签:数论

【题目描述】
组合数表示的是从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;
}




 

本文可以转载,但必须附上原文链接,否则你会终生找不到妹子!!!欢迎关注我的CSDN: ahyjjr