书海扬帆的博客

题面传送门

解题思路


组合数与杨辉三角有着千丝万缕的联系,不得不说小学奥数的知识在OI中用处还是蛮大的。(不知道杨辉三角是啥?)杨辉三角与组合数最重要的一个联系在于,第n行的m个数可表示为$ \left( {\begin{array}{*{20}{c}} {m - 1}\\ {n - 1} \end{array}} \right) $,即为从n-1个不同元素中取m-1个元素的组合数。

这就意味着对于每一次询问,我们无需现算出组合数的值,采用打表来实现即可。这样一来,时间复杂度就大大降低了。

那么预处理好杨辉三角形,还有T组询问怎么办?

我们可以用一个叫做矩阵和(又叫做二维前缀和)的东西(就是下面的S数组)。

给个口诀吧:上加左 减左上 加自己

这样预处理只需要2000*2000的时间,而面对T次询问,每次询问只需要( O(1) )的时间就可以回答,是不是超级方便!

代码分享


read是读入优化函数,然后处理询问的时候用了一个三目运算符,其余的就基本上没有难于理解的地方了。。。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>

using namespace std;
typedef long long ll;
#define R register
#define I inline
template<class T>void read(T &x) {
register int c = getchar(), f = 1; x = 0;
while(!isdigit(c)) {if (c == '-') f = -1; c = getchar();}
while(isdigit(c)) x = x * 10 + c - '0', c = getchar();
x *= f;
}
int n,m,k,t;
int a[2002][2002],s[2002][2002];
int main(){
read(t);read(k);
a[1][1]=1;
for(register int i=0;i<=2000;i++) a[i][0]=1;
for(register int i=2;i<=2000;i++)
{
for(register int j=1;j<=i;j++)
{
a[i][j]=(a[i-1][j-1]+a[i-1][j])%k;
}
}
for(register int i=1;i<=2000;i++)
{
for(register int j=1;j<=i;j++)
{
s[i][j]=s[i-1][j]+s[i][j-1]-s[i-1][j-1];
if(a[i][j]==0) s[i][j]++;
}
s[i][i+1]+=s[i][i];
}
while(t--)
{
read(n);read(m);
m=m>n?n:m;
printf("%d\n",s[n][m]);
}
return 0;
}


本站使用 Material-X 作为主题 , 总访问量为 次 。
载入天数...载入时分秒... 字数统计:730.1k