zoukankan      html  css  js  c++  java
  • P2822 组合数问题

    P2822 组合数问题

    • 标签:NOIp提高组 2016 云端↑
    • 难度: 普及+/提高
    • 时空限制: 1s / 512MB

    题目描述

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

    【子任务】

    思路分析:

    这个题第一反应50分稳了。打打暴力折腾50分出来。n=1的点AC其他TLE...

    样例一好像没什么用处。。。所以看看样例二能搞出什么东西来。

    手推一下样例二:

    egin{matrix}
    C^0_0&&&&&\
    C^0_1&C^1_1&&&&\
    C^0_2&C^1_2&C^2_2&&&\
    C^0_3&C^1_3&C^2_3&C^3_3&&\
    C^0_4&C^1_4&C^2_4&C^3_4&C^4_4&\
    C^0_5&C^1_5&C^2_5&C^3_5&C^4_5&C^5_5
    end{matrix}

    套一下公式。

    忽略m或n等于0的情况,这个时候组合数无意义。

    结合上面的矩阵(无意义处为-1,实际上不必初始化这些位置,保持默认的0即可):

    egin{matrix}
     -1&0&0&0&0&0&0\
     -1&1&0&0&0&0&0\
     -1&2&1&0&0&0&0\
     -1&3&3&1&0&0&0\
     -1&4&6&4&1&0&0\
     -1&5&10&10&5&1&0\
     -1&6&15&20&15&6&1
    end{matrix}

    容易看出这是一个杨辉三角。杨辉三角的一般递推式:

    egin{equation*}f[i][j]=f[i-1][j-1]+f[i-1][j]end{equation*}

    杨辉三角的初始化:

    egin{equation*}f[i][1]=i,f[i][i]=1end{equation*}

    依上建立状态转移方程。

    直接求会爆掉long long,所以边求边模。

    状态转移方程$f[i][j]=(f[i-1][j-1] mod k+f[i-1][j] mod k) mod k$

    这样就求出组合中的每一项了。问题解决了一半。

    第二步,统计。

    两个for循环,统计一下有矩阵中有多少项为0即可。仍然是dp。

    新开一个ans数组,if(!table[i][j])ans[i][j]=ans[i][j-1]+1;else ans[i][j]=ans[i][j-1];

    这样,我们就可以做到$O(n)$的查询了。好像还有$O(1)$的方法,然而看不懂。

     1 #include<cstdio>
     2 using namespace std;
     3 long long table[2005][2005];
     4 long long ans[2005][2005];
     5 int t,k,n,m,a;
     6 int main()
     7 {
     8     scanf("%d%d",&t,&k);
     9     for(int i=1;i<=2000;i++)
    10         table[i][i]=1,table[i][1]=i%k;
    11     for(int i=2;i<=2000;i++)
    12         for(int j=2;j<=i-1;j++)
    13             table[i][j]=(table[i-1][j-1]%k+table[i-1][j]%k)%k;
    14     for(int i=1;i<=2000;i++)
    15         for(int j=1;j<=i;j++)
    16             if(!table[i][j])
    17                 ans[i][j]=ans[i][j-1]+1;
    18             else
    19                 ans[i][j]=ans[i][j-1];
    20     for(int i=1;i<=t;i++)
    21     {
    22         scanf("%d%d",&n,&m);
    23         a=0;
    24         for(int i=1;i<=n;i++)
    25             if(i>m)
    26                 a+=ans[i][m];
    27             else
    28                 a+=ans[i][i];
    29         printf("%lld
    ",a);
    30     }
    31     return 0;
    32 }
    Code
  • 相关阅读:
    LeetCode "Median of Two Sorted Arrays"
    LeetCode "Distinct Subsequences"
    LeetCode "Permutation Sequence"

    LeetCode "Linked List Cycle II"
    LeetCode "Best Time to Buy and Sell Stock III"
    LeetCode "4Sum"
    LeetCode "3Sum closest"
    LeetCode "3Sum"
    LeetCode "Container With Most Water"
  • 原文地址:https://www.cnblogs.com/TheRoadToAu/p/7190526.html
Copyright © 2011-2022 走看看