【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; }