【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

 
 
题解:
这个题目可能是所有day2题目中最水的了吧,首先我们要知道一个公式c[i][j]=c[i-1][j]+c[i-1][j-1],然后给c[i][0]和c[1][1]赋初值1,就能很快求出每个c[i][j],但会爆long long,我们思考如果c[i][j]%k==0,那么必然c[i][j]=k*h+u(h,u为常数),所以能否整除k,关键是看u,如果u可以之后加上某个数整除k那么c[i][j]就可以整除k,所以我们一边取%k一边推并不影响结果,但之后还是会t掉,所以我们每行统计一下答案,最后逐行加起来就可以了。
 
 
代码:
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <iostream>
#define ll long long
#define MAXN 3000
ll c[MAXN][MAXN];
ll ans[MAXN][MAXN];
int n,m,k,t;
using namespace std;
int main()
{
    cin>>t>>k;
    c[1][1]=1;c[1][0]=1;c[0][0]=1;
    for(int i=2;i<=2010;i++){
        for(int j=0;j<=2010;j++){
            if(j==0) c[i][j]=1;
            else c[i][j]=c[i-1][j]+c[i-1][j-1],c[i][j]%=k;
        }
    }
    for(int i=1;i<=2010;i++){
        for(int j=0;j<=2010;j++){
            ans[i][j]=ans[i][j-1];
            if(c[i][j]%k==0) ans[i][j]++;
        }
    }
    while(t--){
        int anss=0;
        cin>>n>>m;
        for(int i=0;i<=n;i++)
            anss+=ans[i][min(i,m)];
        printf("%d\n",anss);
    }
    return 0;
}

 

posted @ 2017-08-15 17:31  人间失格—太宰治  阅读(166)  评论(0编辑  收藏  举报