博客园 首页 私信博主 显示目录 隐藏目录 管理 动画

题解 【NOIP2016】组合数问题

【NOIP2016】组合数问题

Description

Pic

Input

第一行有两个整数t, k,其中t代表该测试点总共有多少组测试数据,k的意义见【问题描述】。
接下来t行每行两个整数n, m,其中n, m的意义见【问题描述】。

Output

t行,每行一个整数代表所有的0<=i<=n,0<=j<=min(i,m)中有多少对(i, j)满足C(j,i)是k的倍数。

Sample Input

输入1:
1 2
3 3

输入2:
2 5
4 5
6 7

Sample Output

输出1:
1

输出2:
0
7

Hint

样例1提示:
在所有可能的情况中,只有C(1,2)是2的倍数。

输出范围:
Pic

Source

NOIP2016 ,数学, 杨辉三角

 

解析

这题看上去确实很难做哈。

毕竟这数据范围。。

但实际上,我们完全可以利用杨辉三角预处理出组合数模k的值,

这样只要模k为0就是k的倍数了。

然后再O(2000*2000)记录下次数就能O(1)回答了。

 

上AC代码:

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

int t,k,m,n;
int f[2002][2002],a[2002][2002];

int main(){
    scanf("%d%d",&t,&k);
    for(int i=0;i<=2000;i++){
        f[i][0]=1%k;
    }
    for(int i=1;i<=2000;i++){
        for(int j=1;j<=i;j++){
            f[i][j]=(ll)(f[i-1][j]+f[i-1][j-1])%k;
        }
    }/*计算杨辉三角模k的值*/
    if(!f[0][0]) a[0][0]=1;
    for(int i=1;i<=2000;i++){
        a[i][0]=a[i-1][0]+(f[i][0]==0);
        for(int j=0;j<i;j++){
            a[i][j]=a[i-1][j]+a[i][j-1]-a[i-1][j-1]+(f[i][j]==0);
            //如果值为0就是k的倍数
        }
        a[i][i]=a[i][i-1]+(f[i][i]==0);
        for(int j=i+1;j<=2000;j++) a[i][j]=a[i][i];
    }/*预处理计数*/
    for(int i=1;i<=t;i++){
        scanf("%d%d",&n,&m);
        printf("%d\n",a[n][m]);
        //O(1)回答
    }
    return 0;
}

 

 

posted @ 2019-03-01 20:54  Hastin  阅读(192)  评论(0编辑  收藏  举报