组合数

#include <iostream>
#include <cstring>
#include <cstdlib>
using namespace std;

const int MAXN=2000,MAXM=2000;

int t,k,table[MAXM][MAXN],ser[MAXM][MAXN],h[MAXN];
//table数组用来打杨辉三角表(边打表边取余)
//取余公式非常简单 (a+b)%c=((a%c)+(b%c))%c;
//ser数组用来存储C(j,i)范围内的可以整除k的个数
//k数组用于动态的存储当前行里有多少个满足条件的C(j,i)
//简单介绍杨辉三角
//nm 0 1 2  3  4  5 6
//0   1
//1   1 1
//2   1 2 1
//3   1 3 3  1
//4   1 4 6  4  1
//5   1 5 10 10 5  1 
//6   1 6 15 20 15 6 1
//上面的横排是C(m,n)中的m
//竖排是C(m,n)中的n
//于是我们可以很简单用一个矩阵+dp求出所有的组合数
//满足公式C(m,n)=C(m,n-1)+C(m-1,n-1)
//当然接下来我的代码忽略了m=0和n=0的情况 
int main(){
    cin>>t>>k;
    //预处理开始 
    for(int i=0;i<MAXM;++i){
        for(int j=0;j<i;++j){//复杂度O( n(n-1)m/2 ) n是MAXN,m是操作步数
            if(j==0){//第一竖行都是i+1 我直接单独处理 
                table[i][j]=(i+1)%k;//杨辉三角的dp求解 
                if(!table[i][j]){
                    ++h[i];//如果满足整除k 就记录在h中
                }
                ser[i][j]=ser[i-1][j]+h[i];//这里dp出C(1,1)到C(i,j)有多少个满足条件的组合数 
                //cout<<ser[i][j]<<" ";
                continue;
            }
            table[i][j]=(table[i-1][j]+table[i-1][j-1])%k; //杨辉三角的dp求解 
            if(!table[i][j]){
                ++h[i];//如果满足整除k 就记录在h中
            }
            ser[i][j]=ser[i-1][j]+h[i];//这里dp出C(1,1)到C(i,j)有多少个满足条件的组合数 
            //cout<<ser[i][j]<<" ";
        }
        //对于C(i,i)也就是j=i的情况单独处理 一定是1 
        table[i][i]=1%k;
        if(i==0&&!table[0][0]){
            ++ser[0][0];
            continue;
        }
        ser[i][i]+=ser[i][i-1];
        if(!table[i][i]){
            ++ser[i][i];
        }
        //cout<<ser[i][i]<<endl;
    }
    for(int i=0;i<t;++i){//查询操作直接O(1)完成 
        int n,m;
        cin>>n>>m;
        --n,--m;
        if(m>n)m=n;//注意m可能大于n 
        cout<<ser[n][m]<<endl; 
    } 
    return 0;
}
原文地址:https://www.cnblogs.com/voldemorte/p/7234794.html