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

    洛谷P2822

    数学真重要啊……

    其实解这一题的关键就是组合恒等式:C(n,m)=C(n-1,m)+C(n-1,m-1),然后再知道组合数的矩阵(杨辉三角)和题中n,m的关系就很容易解决了(然而做这题之前我并不知道组合恒等式于是杯具了)

    由上文提到的几何恒等式,我们可以将组合数打成如下矩阵(C(i,j)行数代表i,列数代表j(均从0开始))

    1

    1 1

    1 2 1

    1 3 3 1

    1 4 6 4 1

    ………………

    接下来我们来看要求的结论:求所有的0≤i≤n,0≤j≤min(i,m)中有多少对(i,j)使C(i,j)是k的倍数。

    我乍一看到min(i,m)以为这题很复杂。但放到图中一看,想到i≥j,我们惊奇地发现,我们要求的结论就是图中共n+1行,m+1列(包括i=0,j=0)中有多少组合数是k的倍数。

    画个图给大家感受一下(灵魂画手)

    比如n=3,m=2,那我们就是要求图中这个部分组合数是k的倍数的数量

    但询问量特别大,所以我们可以预处理一下

    每访问到一个元素都枚举一个矩形很慢,但我们有递推式:d[i][j]=d[i-1][j]+d[i][j-1]-d[i-1][j-1]+!(zuhe[i][j]%k)

    拿上面那个矩阵举例子

    黑色部分的信息(被k整除的组合数个数)d[3][2]可以表示为红色部分的信息d[2][2]加上绿色部分的信息d[3][1]减去蓝色部分的信息d[2][1](此信息满足区间加法和减法性质)

    事实上因为c++数组自动初始化为0的问题,所以我们只会初始化有组合数部分的d值(即i≥j),那么m<n的时候怎么办呢?

    很显然图中黑色部分的信息等于红色部分的信息(因为组合数数量相等)

    所以当m<n时d[m][n]=d[m][m](在递推的时候处理或是在输出的时候处理均可,我是在输出的时候处理的)

    另外此处递推的时候注意一下边界问题(i=j时),此时d[i-1][j]我们没有更新(因为i-1<j),所以此时的d[i][j]=d[i][j-1]+!(zuhe[i][j]%k)

    即黑色部分的信息等于红色部分的信息加上当前点的信息

    还有一点小tip就是2000个组合数会很大,所以我们递推组合数的时候就将它模k,这样模k=0的组合数在表中的值就是0,上面的递推公式就改为:d[i][j]=d[i-1][j]+d[i][j-1]-d[i-1][j-1]+!zuhe[i][j](i>j),d[i][i]=d[i][i-1]+!zuhe[i][i]

    剩下的就很简单了,记得初始化zuhe[1][0]=zuhe[1][1]=1即可

    代码如下:

     1 #include<cstdio>
     2 using namespace std;
     3 int t,k,n,m;
     4 int zuhe[2001][2001];//存储组合数(%k意义下) 
     5 int d[2001][2001];//存储方案个数 
     6 int main()
     7 {
     8     scanf("%d%d",&t,&k);
     9     zuhe[1][0]=zuhe[1][1]=1;//初始化C(1,0)=C(1,1)=1 
    10     for(int i=2;i<=2000;i++)
    11         for(int j=0;j<=i;j++)
    12             zuhe[i][j]=(zuhe[i-1][j]+zuhe[i-1][j-1])%k;
    13     for(int i=1;i<=2000;i++)
    14     {
    15         for(int j=1;j<i;j++)
    16             d[i][j]=d[i-1][j]+d[i][j-1]-d[i-1][j-1]+!zuhe[i][j];
    17         d[i][i]=d[i][i-1]+!zuhe[i][i];
    18     }
    19     while(t--)
    20     {
    21         int n,m;
    22         scanf("%d%d",&n,&m);
    23         if(n<m)
    24             m=n;
    25         printf("%d
    ",d[n][m]);
    26     }
    27     return 0;
    28 }
  • 相关阅读:
    Mesos源码分析(8): Mesos-Slave的初始化
    OpenStack(一)——OpenStack的相关概念
    awk(gawk)文本报告生成器
    echo的色彩处理
    bash命令检测Shell脚本中的语法错误和查看详细执行过程
    Linux命令之cut
    sed流编辑器
    shell中函数的使用
    shell中的shift左移参数命令
    shell中跳出循环语句break和continue
  • 原文地址:https://www.cnblogs.com/LiHaozhe/p/9500834.html
Copyright © 2011-2022 走看看