zoukankan      html  css  js  c++  java
  • 组合数问题 前缀和

    题目描述
    组合数 $inom{n}{m}$ 表示的是从 n 个物品中选出 m 个物品的方案数。举个例子,从 $(1,2,3)$ 三个物品中选择两个物品可以有 $(1,2),(1,3),(2,3)$ 这三种选择方法。根据组合数的定义,我们可以给出计算组合数 $inom{n}{m}$
    的一般公式:

    $inom{n}{m}=frac{n!}{m!(n-m)!}$

    其中 $n!=1 imes2 imescdots imes n$ 特别地,定义 $0!=1$。
    小葱想知道如果给定 n,m 和 k,对于所有的 $0leq ileq n,0leq jleq min left ( i, m ight )$ 有多少对 $(i,j)$ 满足 $k|inom{i}{j}$ 。

    输入格式
    第一行有两个整数 t,k,其中 tt 代表该测试点总共有多少组测试数据,k 的意义见问题描述。

    接下来 t 行每行两个整数 n,m,其中 n,m 的意义见问题描述。

    输出格式
    共 t 行,每行一个整数代表所有的 $0leq ileq n,0leq jleq min left ( i, m ight )$ 中有多少对 (i,j) 满足 $k|inom{i}{j}$。

    输入输出样例
    输入
    1 2
    3 3
    输出
    1
    输入
    2 5
    4 5
    6 7
    输出
    0
    7

    这个题我们主要用了前缀和优化,

    否则会爆T QwQ

    什么是前缀和呢!?

    接下来我们先讲一下什么是前缀和,

    前缀和分两种,(我就知道两种

    一维前缀和 and 二位前缀和

    一维前缀和很明显,就是一条线,

    我们需要开一个数组,

    计算从1~i的i个数的和,

    然后当我们需要求区间x~y的时候

    我们只需要用前y个数的和减去前x-1个数的和便是答案,

    $s[x][y]=a[y]-a[x-1]$

    二维前缀和也是同理,

    只不过是用区间做加减,

    上图

    红色区间(x1,y1)就是我们要求的区间和,

    我们只需要用大矩形(0,0)(x2,y2)减去黄加绿矩形(0,0)(x1,y2)和黄加蓝矩形(0,0)(x2,y1)

    然后因为黄矩形被剪了两次,所以我们再加上小黄(0,0)(x1,y1)

    所以我们的公式就推导出来了($a[i][j]$代表从(0,0)到(i,j)的矩形的数的和

    $s[i][j]=s[i-1][j]+s[i][j-1]-s[i-1][j-1]$

    接下来我们来谈这道题,

    组合数$C(n,m)=inom{n}{m}=frac{n!}{m!(n-m)!}$

    很明显,这是一个阶乘级别的,

    我们都知道,

    阶乘除了爆时间就是爆long long,

    所以我们为了防止爆T,

    可以用前缀和打表,

    这样我们每次询问就只需要O(1)的复杂度,

    我们根据组合数的公式的一条性质

    $C(n,m)=C(n-1,m)+C(n-1,m-1)$

    可以得出组合数公式完全符合一个我们熟知的东东,

    杨辉三角形

    所以我们就可以通过杨辉三角形加前缀和来给这道题打一个表,

    上图

    由上图我们可以明显地看到,红三角形=绿三角形+蓝三角形-黄三角形,

    所以我们就可以导出递推公式

    $s[i][j]=s[i-1][j-1]+s[i][j-1]-s[i-1][j-2]$

    再判断一下$a[i][j]$是否能够被k整除,

    1 for(int i=2;i<=2000;i++){
    2     for(int j=1;j<=i;j++){
    3         c[i][j]=(c[i-1][j]+c[i-1][j-1])%k;
    4         s[i][j]=s[i-1][j]+s[i][j-1]-s[i-1][j-1];
    5         if(c[i][j]==0) s[i][j]++;
    6     }
    7     s[i][i+1]=s[i][i];
    8 }

    还有$s[i][i+1]=s[i][i]$

    因为我们在递推的过程中无法访问到s[i][i+1]

    但我们在下一行的时候当我们到s[i+1][i+2]的时候需要访问到s[i][i+1]

    然后因为当$C(n,m)$中的$m>n$时,$C(n,m)=0$

    但因为我们这里的j是取0~m任何数的,

    所以我们的$s[n][m]=s[n][n]$

    所以我们的s[i][i+1]=s[i][i]

    然后这个题就很容易地写完了。

    容易个**啊,你写了俩小时(还不是因为你太蒻了啊!你个**

    不说了,上代码

     1 #include<cstdio>
     2 #include<algorithm>
     3 using namespace std;
     4 int t,k,n,m,c[2001][2001],s[2001][2001];
     5 int main(){
     6     scanf("%d%d",&t,&k);
     7     c[1][1]=1;
     8     for(int i=0;i<=2000;i++) c[i][0]=1;
     9     for(int i=2;i<=2000;i++){
    10         for(int j=1;j<=i;j++){
    11             c[i][j]=(c[i-1][j]+c[i-1][j-1])%k;
    12             s[i][j]=s[i-1][j]+s[i][j-1]-s[i-1][j-1];
    13             if(c[i][j]==0) s[i][j]++;
    14         }
    15         s[i][i+1]=s[i][i];
    16     }
    17     for(int i=1;i<=t;i++){
    18         scanf("%d%d",&n,&m);
    19         if(m>n) m=n;
    20         printf("%d
    ",s[n][m]);
    21     }
    22     return 0;
    23 }

    时隔一个多月,他终于想起了他的博客园密码

  • 相关阅读:
    iOS 苹果开发证书失效的解决方案(Failed to locate or generate matching signing assets)
    iOS NSArray数组过滤
    App Store2016年最新审核规则
    iOS 根据字符串数目,自定义Label等控件的高度
    iOS 证书Bug The identity used to sign the executable is no longer valid 解决方案
    Entity FrameWork 增删查改的本质
    EF容器---代理类对象
    Entity FrameWork 延迟加载本质(二)
    Entity FrameWork 延迟加载的本质(一)
    Entity FrameWork 增删查改
  • 原文地址:https://www.cnblogs.com/sxy2004/p/12511488.html
Copyright © 2011-2022 走看看