• 组合数


    众所周知(C_n^m=frac{n!}{m!(m-n)!}),特别地,定义 0!=1

    例题在这里:传送门

    level 0

    一开始的思路必然是挨个算组合数,但是那样必然会超时

    level 1

    组合数递推法:
    (C_n^m=C_{n}^{n-m})
    故:
    (C_n^m=C_{n-1}^{m-1}+C_{n-1}^{m})

    所以可以看出组合数其实是个杨辉三角
    (C_0^0=C_0^i=C_1^1=1)
    所以用杨辉三角打表比刚刚那个直接求快很多

    void build()
    {
      c[0][0]=1;
      c[1][0]=c[1][1]=1;//初始化
      for(int i=2;i<=2000;i++)
      {
        c[i][0]=1;
        for(int j=1;j<=2000;j++
          c[i][j]=c[i-1][j-1]+c[i-1][j];//递推公式。
      }
    }
    

    level 2

    取模大法好!!
    ({C_n^m}mod k)=({C_{n-1}^{m-1}mod k}+{C_{n-1}^{m}mod k})

    void build()
    {
      c[0][0]=1;
      c[1][0]=c[1][1]=1;
      for(int i=2;i<=2000;i++)
      {
        c[i][0]=1;
        for(int j=1;j<=2000;j++)
          c[i][j]=(c[i-1][j-1]%k+c[i-1][j]%k)%k;//重中之重,绝不能盲目地模。
      }
    }
    

    level 3

    dl“瞎搞”出的玄学优化(呜呜呜我什么时候能像大佬一样厉害)

    因为取模运算比四则运算都耗时,所以大佬想尽一切办法减少取模
    所以在一开始膜的时候就判断一下

    void build()
    {
      c[0][0]=1;
      c[1][0]=c[1][1]=1;
      for(int i=2;i<=2000;i++)
      {
        c[i][0]=1;
        for(int j=1;j<=2000;j++)
        {
          c[i][j]=(c[i-1][j-1]+c[i-1][j])%k;
          if(c[i][j]%k==0)s[i][j]=1;//先记一下,如果已经满足条件,就标记一下,省去了更多的计算。
        }
      }
    }
    void solve()
    {
      t=read(),k=read();
      build();
      while(t--)
      {
        ans=0;
        n=read(),m=read();
        for(int i=0;i<=n;i++)
          for(int j=0;j<=my_min(i,m);j++)
            ans+=s[i][j];
        printf("%lld
    ",ans);
      }
    }
    

    level 4

    谁能想到用前缀和计算组合数呢

    二维的前缀和记住:上加左减左上加自己

    即:ans[i][j]=ans[i][j-1]+ans[i-1][j]-ans[i-1][j-1]

    这一句是处理边界要加上的:ans[i][i+1]=ans[i][i]

    这个题是一个裸的前缀和!!所以你求出ans[i][j]直接就是答案了

    inline void build()
    {
      c[0][0]=1;
      c[1][0]=c[1][1]=1;
      for(int i=2;i<=2000;i++)
      {
        c[i][0]=1;
        for(int j=1;j<=i;j++)
        {
          c[i][j]=(c[i-1][j-1]+c[i-1][j])%k;
          ans[i][j]=ans[i-1][j]+ans[i][j-1]-ans[i-1][j-1];//前缀和(没加自己是因为自己本来就是0)
          if(!c[i][j])ans[i][j]++;//如果满足结论,计数加一
        }
        ans[i][i+1]=ans[i][i];//弥补杨辉三角
      }
    }
    inline void solve()
    {
      t=read(),k=read();
      build();
      while(t--)
      {
        n=read(),m=read();
        if(m>n)printf("%lld
    ",ans[n][n]);//如果m>n,ans只会达到n,只需输出ans[n,n]就可以了。
        else printf("%lld
    ",ans[n][m]);
      }
    }
    

    最后!
    感谢大佬的博客!!!

    为了自己,和那些爱你的人
  • 相关阅读:
    【cs231n笔记】assignment1之KNN
    【数据】常用卫星遥感数据下载地址整理
    ArcGIS中重采样栅格像元匹配问题
    python调用HEG工具批量处理MODIS数据
    Google Earth Engine学习资源分享
    地表温度反演的单通道方法辩异
    Android Studio无法下载sdk的问题
    Anaconda常用命令
    AMSR-E/AMSR-2数据介绍与下载
    NASA HEG tool安装心得
  • 原文地址:https://www.cnblogs.com/zhmlzhml/p/14412872.html
Copyright © 2020-2023  润新知