【NOIP2016】組合數問題

無限遞增

目錄

題目

描述

輸入

輸出

樣例輸入

樣例輸出

數據範圍

分析

代碼


題目

描述

組合數表示的是從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 2
3 3

樣例輸出

1

數據範圍

 

分析

很明顯,如果暴力的話是會超時的。但我們如果仔細研究一下組合數的性質的話,就會發現它和楊輝三角有異曲同工之妙。

我們設C[i][j]爲C_{i}^{j}的答案。

其實通過打表發現,它其實就是楊輝三角。

公式:C[i][j] = C[i - 1][j] + C[i - 1][j -1];

但是楊輝三角的增長速度是非常迅速的。

但題目求的是C_{i}^{j}是 k 的倍數,這就意味着k | C_{i}^{j}。所以我們可以想到在求C數組時,算出C[i][j]模k的值,如果此時C[i][j] = 0 就表明C_{i}^{j}是 k 的倍數,但這樣做還是會炸掉,我們就可以藉助數論知識

(a + b) % k = (a % k + b % k) % k 

接着我們利用前綴和的思想用一個數組S來統計答案。

公式:S[i][j] = S[i][j - 1] + S[i - 1][j] - S[i - 1][j - 1];

爲什麼要減S[i - 1][j - 1]呢?

假如公式爲S[i][j] = S[i][j - 1] + S[i - 1][j];

S[i][j] = S[i][j - 1] + S[i - 1][j] ;

S[i][j] = S[i][j - 2] + S[i - 1][j - 1] + S[i - 1][j - 1] + S[i - 2][j];

……

可以發現:這裏面出現了兩次S[i - 1][j - 1],爲了避免重複,所以我們需要減去一個S[i - 1][j - 1];

代碼

#include<cstdio>
#define M 2000
#define reg register
 
inline void read(int &x){
    x = 0;
    int f = 1;
    char s = getchar();
    while (s < '0' || s > '9'){
        if (s == '-')
            f = - 1;
        s = getchar();
    }
    while (s >= '0' && s <= '9'){
        x = x * 10 + s - '0';
        s = getchar();
    }
    x *= f;
}
 
int t,k,n,m;
int C[M + 5][M + 5],Ans[M + 5][M + 5];
 
int main(){
    read(t),read(k);
    for (reg int i = 1;i <= M; ++ i)
        C[i][0] = C[i][i] = 1;
    for (reg int i = 1;i <= M; ++ i)
        for (reg int j = 1;j < i; ++ j)
            C[i][j] = (C[i - 1][j - 1] % k + C[i - 1][j] % k) % k;
    for (reg int i = 1;i <= M; ++ i)
        for (reg int j = 1;j <= M; ++ j){
            Ans[i][j] = Ans[i - 1][j] + Ans[i][j - 1] - Ans[i - 1][j - 1];
            if (j <= i && ! C[i][j])
                 ++ Ans[i][j];
        }
    for (reg int i = 1;i <= t; ++ i)
    {
        read(n),read(m);
        printf("%d\n",Ans[n][m]);
    }
    return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章