• 洛谷P2822 组合数问题(题解)


    https://www.luogu.org/problemnew/show/P2822(题目传送)

    先了解一下有关组合数的公式:(m在上,n在下)

    组合数通项公式:C(n,m)=n!/[m!(n-m)!]=(n-m+1)!/m!

    组合数递推公式:C(n,m)=C(n-1,m-1)+C(n-1,m)

    发现组合数的递推的直观图像形式就是杨辉三角(第i行第j列的数等于C(i-1,j-1))

    由于题目要求多组组合数,便可以递推组合数做预处理(直接用通项公式算什么的太粗暴(慢)了)。一看数据范围,保证让普通范围溢出的节奏啊,但定心一看,我们只用知道每个组合数是否能整除k就可,所以可以每次递推算组合数时模k。  TIP:递推不要忘了初始状态(边界)

    求出组合数后,发现如果对每次询问都从头到尾扫一遍的话保准会超时,便想到了一个能有效减少查询统计时的复杂度,每一次查询O(n)降到O(1)的神器——前缀和。求一个矩阵的前缀和,只要记住公式:上加左,减上左,加自己。

    AC代码如下:

     1 #include<iostream>
     2 #include<cstdio>
     3 using namespace std;
     4 int c[2002][2002],n[10001],m[10001],dp[2001][2001];
     5 int main()
     6 {
     7     int t,k,mmax=0,nmax=0;
     8     cin>>t>>k;
     9     for(int i=1;i<=t;i++)
    10     {
    11         scanf("%d%d",&n[i],&m[i]);
    12         if(nmax<n[i]) nmax=n[i];
    13         if(mmax<m[i]) mmax=m[i];
    14     }    
    15     c[1][1]=1;
    16     int mmaxb=mmax;
    17     if(mmax>nmax) mmax=nmax+1;
    18     else mmax++;
    19     for(int i=2;i<=nmax+1;i++)
    20         for(int j=1;j<=min(i,mmax);j++)
    21         {
    22             c[i][j]=(c[i-1][j-1]+c[i-1][j])%k;
    23             if(!c[i][j]) dp[i-1][j-1]=1;
    24         }//这里用杨辉三角递推的组合数,***需要多做一行***,其实没有直接推组合数方便。
    25     for(int j=1;j<=mmaxb;j++) dp[0][j]+=dp[0][j-1];
    26     for(int i=1;i<=nmax;i++)
    27         for(int j=0;j<=mmaxb;j++)
    28         {
    29             if(!j) dp[i][j]+=dp[i-1][j];
    30             else
    31                 dp[i][j]+=dp[i-1][j]+dp[i][j-1]-dp[i-1][j-1];
    32         }
    33     for(int i=1;i<=t;i++) cout<<dp[n[i]][m[i]]<<endl;
    34     return 0;
    35 }
  • 相关阅读:
    【BZOJ】1486 [HNOI2009]最小圈
    【网络流24题】
    【网络流24题】魔术球问题
    【网络流24题】最小路径覆盖问题
    【BZOJ】1026 [SCOI2009]windy数
    【SPOJ】2319 BIGSEQ
    【SPOJ】1182 Sorted bit sequence
    虔诚的墓主人(bzoj 1227)
    Round Numbers(poj 3252)
    windy数(bzoj 1227)
  • 原文地址:https://www.cnblogs.com/InductiveSorting-QYF/p/10623670.html
Copyright © 2020-2023  润新知