【NOIP2016】组合数问题 题解(组合数学+递推)

题目链接

题目大意:给定$n,m,k$,求满足$k|C_i^j$的$C_i^j$的个数。$(0\leq i\leq n,1\leq j\leq \min(i,m))$。

----------------

关于组合数的递推不难想到。简略证明一下。

证明:$C_i^j=C_{i-1}^j+C_{i-1}^{j-1}$。

$  C_{i-1}^j+C_{i-1}^{j-1}$

$=\frac{(i-1)!}{j!(i-j-1)!}+\frac{(i-1)!}{(j-1)!(i-j)!}$

$=\frac{(i-1)!*(i-j)}{j!*(i-j)!}+\frac{(i-1)!*j}{j!(i-j)!}$

$=\frac{(i-1)!*(j+i-j)}{j!*(i-j)!}$

$=\frac{i!}{j!(i-j)!}$

$=C_i^j$

递推出的结果再$mod k$就可以拿到90分了,大部分人考场上就可以过了,有时间可以再深究一下。

满分还要有一个递推式:$ans[i][j]=ans[i-1][j]+ans[i][j-1]-ans[i-1][j-1]$。打表可以得到。

注意边界。时间复杂度$O(n^2)$。$O(1)$查询。

代码:

#include<bits/stdc++.h>
using namespace std;
long long c[2005][2005],ans[2005][2005];
int k,n,m,t;
int main()
{
    scanf("%d%d",&t,&k);
    c[0][0]=c[1][0]=1;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];
            if (!c[i][j]) ans[i][j]++;
        }
        ans[i][i+1]=ans[i][i];
    }
    while(t--)
    {
        scanf("%d%d",&n,&m);
        if (m>n)printf("%lld\n",ans[n][n]);
        else printf("%lld\n",ans[n][m]);
    }
    return 0;
} 

 

posted @ 2020-05-20 19:52  我亦如此向往  阅读(176)  评论(0编辑  收藏  举报