嘘~ 正在从服务器偷取页面 . . .

洛谷P2822 [NOIP2016 提高组] 组合数问题 题解


洛谷P2822 [NOIP2016 提高组] 组合数问题 题解

题目链接:P2822 [NOIP2016 提高组] 组合数问题

题意

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

\[ \binom{n}{m}=\frac{n!}{m!(n-m)!} \] 其中 \(n!=1\times2\times\cdots\times n\);特别地,定义 \(0!=1\)

小葱想知道如果给定 \(n,m\)\(k\),对于所有的 \(0\leq i\leq n,0\leq j\leq \min \left ( i, m \right )\) 有多少对 \((i,j)\) 满足 \(k|\binom{i}{j}\)

  • 对于全部的测试点,保证 \(0 \leq n, m \leq 2 \times 10^3\)\(1 \leq t \leq 10^4\)

显然杨辉三角的那个预处理

询问可以用二维前缀和来搞

这里的前缀和有个有趣的地方

s[i][j]=s[i-1][j]+s[i][j-1]-s[i-1][j-1]+(c[i][j]==0)

这里在获取 \(s_{i-1,j}\) 的时候,如果 \(j=i\) ,那 \(s_{i-1,j}\) 就没有算,会导致出错

解决的方法很简单,直接在每次计算时把 \(s_{i,i+1}\leftarrow s_{i,i}\) 就好了

这样的话没有影响前缀和数组的意义,蛮好

代码:

#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <iomanip>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
#define N (int)(2e3+15)
#define _ 2000
int k,c[N][N],s[N][N];
void init()
{
    c[1][1]=1;
    for(int i=0; i<=_; i++) c[i][0]=1;
    for(int i=2; i<=_; i++)
        for(int j=1; j<=i; j++)
            c[i][j]=(c[i-1][j]+c[i-1][j-1])%k;
    for(int i=2; i<=_; i++)
    {
        for(int j=1; j<=i; j++)
            s[i][j]=s[i-1][j]+s[i][j-1]-s[i-1][j-1]+(c[i][j]==0);
        s[i][i+1]=s[i][i];
    }
}
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    // freopen("check.in","r",stdin);
    // freopen("check.out","w",stdout);
    int Q,n,m;
    cin >> Q >> k;init();
    while(Q--)
    {
        cin >> n >> m;
        if(m>n)m=n;
        cout << s[n][m] << '\n';
    }
    return 0;
}

文章作者: q779
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-ND 4.0 许可协议。转载请注明来源 q779 !
评论
  目录