组合数问题(NOIP2016提高组Day2T1)

【题目描述】
组合数表示的是从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】
1 2
3 3
【样例输出1】
1
【样例输入2】
2 5
4 5
6 7
【样例输出2】
0
7
【数据范围】

【分析】
首先我们要知道一个关于组合数的递推公式:C(i,j)=C(i-1,j)+C(i-1,j-1),其实就是杨辉三角,只是初始化不一样。
如果要计算C(2000,2000),只能用高精度计算,但是这道题并不需要计算出每一个C(i,j)的值,只要知道C(i,j)对于k的余数即可。
于是我们可以设计算法:先计算出每一个C(i,j)对k取模的值,再用f(i,j)表示C(i,j)是否能整除k,是就为1,反之为0,最后用ans(i,j)表示f(1..i,1..j)的和,最后读入n,m就直接输出ans(n,m)即可。
程序中为了减少内存,把ans数组消掉了。

const maxn=2000;
var
  f,flag:array[0..2001,0..2001]of longint;//其实i和j如果有一个为0,C(i,j)就不存在了,因为分母为零,不过C(0,0)好像是1
    i,j,t,k,n,m:longint;
begin
  read(t,k);
  for i:=1 to maxn do begin f[i,1]:=i mod k;f[i,i]:=1; end;
    for i:=3 to maxn do
      for j:=2 to i-1 do
          f[i,j]:=(f[i-1,j]+f[i-1,j-1]) mod k;
    for i:=1 to maxn do
      for j:=1 to i do
          if f[i,j]=0 then flag[i,j]:=1;
    fillchar(f,sizeof(f),0);
    for i:=1 to maxn do
      for j:=1 to maxn do
          f[i,j]:=f[i-1,j]+f[i,j-1]-f[i-1,j-1]+flag[i,j];
    for i:=1 to t do begin
      read(n,m);
        writeln(f[n,m]);
    end;
end.
posted @ 2016-11-21 12:10  JRX2015U43  阅读(265)  评论(0编辑  收藏  举报