NOIP2016 组合数问题

题目大意:组合数 Cmn 表示的是从 n 个物品中选出 m 个物品的方案数。举个例子,从(1 , 2 , 3)三个物品中选择两个物品可以有( 1 , 2 ) , ( 1 , 3 ) , ( 2 , 3 ) 这三种选择方法。根据组合数的定 义,我们可以给出计算组合数的一般公式:

Cmn=n!m!(nm)!

其中 n! = 1 × 2 × · · · × n。
小葱想知道如果给定 n , m 和 k,对于所有的 0 ≤ i ≤ n,0 ≤ j ≤ min ( i , m ) 有多少对 ( i , j ) 满足 Cji 是k的倍数。

第一行有两个整数 t , k,其中 t 代表该测试点总共有多少组测试数据。接下来 t 行,每行两个整数 n , m ( 1 ≤ n , m ≤ 2000 , 1 ≤ t ≤ 104 , 2 ≤ k ≤ 21 )。n , m , k 的意义见题目描述。

题目分析:
这道题数据范围特别小,基本上属于暴力出答案。
首先,我们预处理出每一个 Cjimodk 的值,可以由杨辉三角形的性质求出所有的值。同时,建立一个新数组判断这个位置对应的总合法数量(根据模 k 是否等于 0 判断每一个单独的 ( i , j ),之后和前面的加起来统计在一起,统计的方式可以自己想,很简单的)。最后 O(1) 查询并输出答案。

下面附上代码:

[cpp] view plain copy
print ?
  1. #include  
  2. const int MX = 2005;  
  3.   
  4. int n,m,T,k,C[MX][MX],dv[MX][MX],column[MX];  
  5. //dv:符合条件的总方案数    column:某个时刻,每一列的合法数   
  6.   
  7. void preprocess(){  
  8.     C[0][0] = 1;  
  9.     for (int i = 1;i <= 2000;i++)  
  10.         C[i][0] = 1;  
  11.     for (int i = 1;i <= 2000;i++){  
  12.         for (int j = 1;j <= i;j++){  
  13.             C[i][j] = (C[i - 1][j]+C[i - 1][j - 1])%k;  
  14.             if (C[i][j] == 0){  
  15.                 column[j]++;  
  16.             }  
  17.             dv[i][j] = dv[i][j - 1]+column[j];  
  18.               
  19.         }  
  20.     }  
  21. }  
  22.   
  23. int main(){  
  24.     scanf(”%d%d”,&T,&k);  
  25.     preprocess();  
  26.     while (T–){  
  27.         scanf(”%d%d”,&n,&m);  
  28.         if (m > n)  
  29.             m = n;  
  30.         printf(”%d\n”,dv[n][m]);  
  31.     }  
  32.     return 0;  
  33. }  

你可能感兴趣的:(排列组合,NOIP2016)