P2822 组合数问题

传送门

思路:

  利用公式: C( n,r ) = C( n-1,r ) + C( n-1,r-1 )

  由此可以将计算 C( n,r ) 的过程化为加法来做。

  可以看出,C( n,r ) 其实就是求杨辉三角的第 n 行、第 r 列上的数(行列从 0 开始)。

  先 N暴力地预处理出杨辉三角的各个项,用前缀和记录每一项之前能被 k 整除的排列对数。

  对于每次询问,只要 O(1) 的时间,就能输出答案。

Code:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<string>
#include<cstdlib>
#include<stack>
#include<vector>
#include<queue>
#include<deque>
#include<map>
#include<set>
using namespace std;
#define lck_max(a,b) ((a)>(b)?(a):(b))
#define lck_min(a,b) ((a)<(b)?(a):(b))
typedef long long LL;
const int maxn=2003;
LL n,m,k,T,c[maxn][maxn],f[maxn][maxn];
inline LL read()
{
    LL kr=1,xs=0;
    char ls;
    ls=getchar();
    while(!isdigit(ls))
    {
        if(!(ls^45))
            kr=-1;
        ls=getchar();
    }
    while(isdigit(ls))
    {
        xs=(xs<<1)+(xs<<3)+(ls^48);
        ls=getchar();
    }
    return xs*kr;
}
inline void out(LL xs)
{
    if(!xs) {putchar(48); return;}
    if(xs<0) putchar('-'),xs=-xs;
    LL kr[57],ls=0;
    while(xs) kr[++ls]=xs%10,xs/=10;
    while(ls) putchar(kr[ls]+48),ls--;
}
inline void work()
{
    c[1][1]=1;
    for(LL i=0;i<maxn;i++) c[i][0]=1;
    for(LL i=2;i<maxn;i++)
        for(LL j=1;j<=i;j++)
            c[i][j]=(c[i-1][j]+c[i-1][j-1])%k;
    for(LL i=2;i<maxn;i++)
    {
        for(LL j=1;j<=i;j++)
        {
            f[i][j]=f[i-1][j]+f[i][j-1]-f[i-1][j-1];
            if(c[i][j]==0) f[i][j]+=1;
        }
        f[i][i+1]=f[i][i];
    }
}
int main()
{
    freopen("problem.in","r",stdin);
    freopen("problem.out","w",stdout);
    T=read();k=read();
    work();
    while(T--)
    {
        n=read();m=read();
        m=lck_min(n,m);
        out(f[n][m]),putchar('
');
    }
    fclose(stdin);
    fclose(stdout);
return 0;
}
原文地址:https://www.cnblogs.com/lck-lck/p/9862284.html