• Noip2016day2 组合数问题problem


    题目描述

    组合数表示的是从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

    说明

    【样例1说明】

    在所有可能的情况中,只有是2的倍数。

    【子任务】

     

    思路

    这是一道数论题

    首先要知道组合数的一般递推公式,它的递推公式和杨辉三角是一样的

    c[i][j]=c[i-1][j-1]+c[i-1][j]

    (解释:c[i][j]即为从i件物品中选j件的方案数。如果第i件物品不选,方案数就变为c[i-1][j],如果选第i件物品,方案数就变为c[i-1][j-1],总方案数就为两种情况的方案数之和)

    为了不爆long long,每次求出c[i][j]后先模一下k

    为了节约时间,进行二维求和,最后直接查找答案

    #include<iostream>
    using namespace std;
    int n,m,t,k;
    int c[2001][2001],s[2001][2001];
    void get_c()
    {
        for(int i=0;i<=2000;i++)
        {
            for(int j=0;j<=i;j++)
            {
                if(i==0&&j==0)c[i][j]=1%k;
                else
                {
                    c[i][j]=(c[i-1][j]+c[i-1][j-1])%k;
                }
            }
        }
    }
    void get_s()
    {
        if(c[0][0]==0)s[0][0]=1;
        for(int i=0;i<=2000;i++)
        {
            for(int j=0;j<=i;j++)
            {
                if(i==0&&j==0)continue;
                else
                {
                    if((i==0&&j)||(i==j)){
                        s[i][j]=s[i][j-1];
                        if(c[i][j]==0)s[i][j]++;
                    }
                    else if(i&&j==0){
                        s[i][j]=s[i-1][j];
                        if(c[i][j]==0)s[i][j]++;
                    }
                    else if(i&&j)
                    {
                        s[i][j]=s[i-1][j]+s[i][j-1]-s[i-1][j-1];
                        if(c[i][j]==0)s[i][j]++;
                    }
                }
            }
        }
    }
    int main()
    {
        cin>>t>>k;
        get_c();
        get_s();
        while(t--)
        {
            cin>>n>>m;
            cout<<s[n][min(n,m)]<<endl;
        }
    }
  • 相关阅读:
    【题解】洛谷P1896 [SCOI2005] 互不侵犯(状压DP)
    [BZOJ4383][POI2015] Pustynia-[线段树+dp+拓扑排序]
    [agc016E]Poor Turkeys
    [arc082E]ConvexScore-[凸包]
    [BZOJ4011][HNOI2015]落忆枫音-[dp乱搞+拓扑排序]
    [arc062E]Building Cubes with AtCoDeer
    [arc079F]Namori Grundy
    [agc006F]Blackout
    [BZOJ4444][SCOI2015]国旗计划-[ST表]
    [BZOJ1007][HNOI2008]水平可见直线-[凸包]
  • 原文地址:https://www.cnblogs.com/thmyl/p/6131306.html
Copyright © 2020-2023  润新知