[noip2016]组合数问题<dp+杨辉三角>
题目链接:https://vijos.org/p/2006
当时在考场上只想到了暴力的做法,现在自己看了以后还是没思路,最后看大佬说的杨辉三角才懂这题。。。
我自己总结了一下,我不能反应出杨辉三角的递推是因为对组合C和排列S不熟悉导致的,这些公式基本都是我的短板
从杨辉三角形看出,杨辉三角的i,j位(有0位)就是在i个数选j个出来的方案数,,我们来看下杨辉三角吧
------------------------------------------------------------------------------------------------------------------------------
1
1 1
1 2 1
1 3 3 1
1 4 6 4 1
1 5 10 10 5 1
1 6 15 20 15 6 1
………………………………………………
--------------------------------------------------------------------------------------------------------------------------------
只需要赋初值最上面那个a[0][0]为1就可以预处理出这个2000*2000的图了。。。。
输入n,m只需要判断0<=i<=n和0<=j<=min(n,m)的所有数是不是k的倍数,这个来源就是n,m的左方和上方的数,用以n,m为原点的坐标系说就是第二象限的值,包括x负半轴y正半轴
所以维护一个line[j]表示到当前为止,第j列有几个是k的倍数,然后就是自己这列的个数加上num[i-1][j]
num[i][j]=num[i-1][j]+line[j]
然后就可以愉快的做这道题了,最后提醒一点
所有的杨辉三角的值对k取个模,因为我们只是找k个倍数
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<queue> 5 #include<cmath> 6 #include<iostream> 7 #include<cstdlib> 8 #define maxn 2005 9 using namespace std; 10 11 int t,k,a[maxn][maxn]; 12 int num[maxn][maxn]; 13 int n,m,line[maxn]; 14 15 void show(int x){ 16 for(int i=0;i<=x;i++){ 17 for(int j=0;j<=i;j++){ 18 printf("%d ",a[i][j]); 19 } 20 printf("\n"); 21 } 22 } 23 24 void show2(int x){ 25 for(int i=0;i<=x;i++){ 26 for(int j=0;j<=i;j++){ 27 printf("%d ",num[i][j]); 28 } 29 printf("\n"); 30 } 31 } 32 33 int main(){ 34 for(int i=0;i<=2000;i++) 35 a[i][0]=1; 36 scanf("%d%d",&t,&k); 37 for(int i=1;i<=2000;i++){ 38 for(int j=1;j<=i;j++){ 39 a[i][j]=(a[i-1][j-1]+a[i-1][j])%k; 40 int look=a[i][j]; 41 if(a[i][j]==0){//是k倍数 42 line[j]++; 43 } 44 num[i][j]=num[i][j-1]+line[j]; 45 } 46 } 47 // show(6); 48 // show2(6); 49 for(int i=1;i<=t;i++){ 50 scanf("%d%d",&n,&m); 51 m=min(m,n); 52 printf("%d\n",num[n][m]); 53 } 54 //show函数只是用来输出这个表,看下有没有误而已,可以删去 55 }
总结:很多一看就知道爆数组爆longlong的题,把结果用表格对应写出来,说不定会有意想不到的惊喜