组合数问题

 原题链接:https://www.luogu.org/problem/show?pid=2822#sub

做一次不爽一次。仅仅是对我去年考场上没想起来c(i,j) = c(i-1,j) + c(i-1,j-1)的垃圾行为而感到绝望的愤怒。

(LaTeX用得还暂时不熟练,各位谅解)

我实在是想不起来我当时是有多大的勇气,在这道题写完暴力不管不问的情况下就去刚T2T3。

如果您还没学排列组合,那这道题可能暂时不适合您来做。

如果您学过排列组合(高中数学选修2-3相关内容),应该对一个式子不陌生,就是我刚才在上面写的那个c(i,j) = c(i-1,j) + c(i-1,j-1)。

这是组合数的一个性质,要完成这道题需要用这个式子进行一下预处理。

暴力思想很简单,暴力算阶乘,暴力算组合。如果在暴力时稍加改进会如何?

看到n只有2000,所以可以考虑先把这个范围内的所有c[i][j]都先求出来,就用那个公式,全都推出来就好,边界是c[i][0] = c[i][i] = 1(依据组合数的性质)

这样我们就有了一个表。如果您学过排列组合,可以输出一下看看,这个表长什么样?

这可是杨辉三角啊!

实际上,为了求解方便,在推的时候可以把值对k取模,这样推出来后的c[i][j]如果是0,那么这个就是k的倍数。

按要求输出即可,这题到此结束。

哦?是吗?那你可只有90分啊。

后面那10分卡在哪里了?输出。

如果不加任何处理,在输出的时候我们也是要遍历一次c数组的,这个操作比较费时间。

既然c数组可以打表,答案为什么不可以?

令s[i][j]表示在所有的c(i,j) (1≤j≤i)的里面,为k的倍数的有多少个,那么处理数组的时候就是s[i][j] = s[i][j-1] ,每找到一个s[i][j]为0就让值+1。

按要求输出即可,这题到此结束。

行了,这次可以A了。

参考代码:

 1 #include <iostream>
 2 #define maxn 2005
 3 using namespace std;
 4 int c[maxn][maxn];
 5 int s[maxn][maxn];
 6 int ans[maxn][maxn];
 7 int t,k,n,m;
 8 int main(){
 9     cin >> t >> k;
10     for (int i=1;i<maxn;i++){
11         c[i][0] = 1;
12         c[i][i] = 1;
13         for (int j=1;j<i;j++)
14             c[i][j] = (c[i-1][j] + c[i-1][j-1]) % k;
15     }
16 
17     for (int i=1;i<maxn;i++)
18         for (int j=1;j<=i;j++){
19             s[i][j] = s[i][j-1];
20             if (c[i][j] == 0)
21                 s[i][j]++;
22         }
23 
24     for (int time=1;time<=t;time++){
25         cin >> n >> m;
26     
27         int ans = 0;
28         for (int i=1;i<=n;i++){
29             int j = min(i,m);
30             ans += s[i][j];
31         }
32         cout << ans << endl;
33     
34     
35     }    
36     return 0;
37 }

 

posted @ 2017-09-13 23:55  ShawnZhou_Aether  阅读(368)  评论(0编辑  收藏  举报