[NOIP2016真题]组合数问题

先给一个数据不水的提交地址:http://uoj.ac/problem/263

题目背景
NOIP2016 提高组 Day2 T1

题目描述
组合数这里写图片描述表示的是从 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 行,每行一个整数代表所有的 0≤i≤n,0≤j≤min(i,m) 中有多少对 (i,j) 满足这里写图片描述是 k 的倍数。

样例数据1
输入
1 2
3 3
输出
1
样例数据2
输入
2 5
4 5
6 7
输出
0
7

备注
【样例1说明】
在所有可能的情况中,只有这里写图片描述是 2 的倍数。

【数据规模与约定】
[NOIP2016真题]组合数问题_第1张图片

分析:要是当年学了杨辉三角该多好orz,这道题直接预处理出2000*2000的杨辉三角,并利用前缀和算出每个(i,j)有多少满足条件的个数,最后询问直接输出就好。
前缀和:
[NOIP2016真题]组合数问题_第2张图片
如图所示,第一行是c[0][0],第二行是c[1][0]、c[1][1],以此推类。前缀和累加(num[i][j])就是这个位置所在列有多少满足(line[j])+它左侧上侧所有点满足条件的(num[i][j-1])。图片来自百度百科。

代码:

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;

int getint()
{
    int f=1,sum=0;
    char ch;
    for(ch=getchar();(ch<'0'||ch>'9')&&ch!='-';ch=getchar());
    if(ch=='-')
    {
        f=-1;
        ch=getchar();
    }
    for(;ch>='0'&&ch<='9';ch=getchar())
        sum=(sum<<3)+(sum<<1)+ch-48;
    return sum*f;
}

int c[2010][2010],num[2010][2010],line[2010];
int t,k,m,n; 

void prework()
{
    for(int i=0;i<=2000;++i)
        c[i][0]=1;

    for(int i=1;i<=2000;++i)
        for(int j=1;j<=i;++j)//预处理杨辉三角,表示从i个中选j个
        {
            c[i][j]=c[i-1][j-1]+c[i-1][j];
            c[i][j]%=k;//因为数据到后面太大,直接对k取模
            if(c[i][j]==0)//记录本列到当前位置有多少满足条件的
                line[j]++;
            num[i][j]=num[i][j-1]+line[j];//记录(i,j)中满足条件的
        }
}

int main()
{
    freopen("problem.in","r",stdin);
    freopen("problem.out","w",stdout);

    t=getint();k=getint();

    prework();

    for(int i=1;i<=t;++i)
    {
        n=getint();m=getint();
        if(m>n)//因为只处理到(n,n)所以如果m>n就直接输出num[n][n]
            m=n;
        cout<return 0;
}

本题结。

你可能感兴趣的:(NOIP2016)