您的位置:首页 > 其它

NOIP 2016 Senior 4 - 组合数问题

2017-07-27 16:29 288 查看


这道题。。。其实我第一反应是爆搜。由于组合数的计算有多种公式,思路往往会禁锢,这不,题目故意把公式给了你,让你往这个公式上去想,不超时才怪。。。

在使用该公式进行单行递推时,能够轻松地通过一个点,但是对于10000组测试数据就无能为力了,同时,这种方法还受溢出的限制,因此不可取。如果用这种方法应该能得到60分。

正确的做法应该是使用递推公式。因为递推公式是加法,而通项公式是乘法和除法。加法让求余有了可能。观察发现,题目要求在一开始就输入k,而不是每组数据一个,这更坚定了使用递推公式提前初始化好的信心。

再看数据,发现2000的规模是允许我们以O(n2)的时间复杂度进行初始化的。我们必须要做到查询的时间复杂度降到O(1),才能让初始化有意义(2000*2000也不小了)。

首先我们来看看如何判断一个组合数是不是k的倍数。很简单,模k后判断是否为0即可。接下来就是查询,这时我们应该很自然地想到了二维和数组,到这里这道题就简单了。记住用之前写的暴力多测试几下!!!= =

参考代码(稍慢,可以根据题目给的n,m的最大值进行优化,还可以把long long改成int,因为懒所以我就不改了)

#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <vector>
#include <string>
#include <stack>
#include <queue>
#include <deque>
#include <map>
#include <set>
#include <bitset>
using std::cin;
using std::cout;
using std::endl;
typedef unsigned long long INT;
inline INT readIn()
{
INT a = 0;
bool minus = false;
char ch = getchar();
while(!(ch == '-' || ch >= '0' && ch <= '9')) ch = getchar();
if(ch == '-')
{
minus = true;
ch = getchar();
}
while(ch >= '0' && ch <= '9')
{
a *= 10;
a += ch;
a -= '0';
ch = getchar();
}
if(minus) a = -a;
return a;
}

const INT maxn = 2005;
INT mod;
INT n, m;
INT C[maxn + 5][maxn + 5];
INT f[maxn + 5][maxn + 5];

void run()
{
INT a = readIn();
mod = readIn();
for(int i = 1; i <= maxn; i++)
{
C[i][0] = C[i][i] = 1;
for(int j = 1; j < i; j++)
{
C[i][j] = (C[i - 1][j - 1] + C[i - 1][j]) % mod;
}
}
for(int i = 1; i <= maxn; i++)
{
for(int j = 0; j <= maxn; j++)
{
if(j)
{
f[i][j] = f[i - 1][j] + f[i][j - 1] - f[i - 1][j - 1] + (j <= i && !C[i][j]);
}
else
{
f[i][j] = f[i - 1][j] + (j <= i && !C[i][j]);
}
}
}

while(a--)
{
INT ans = 0;
n = readIn();
m = readIn();
cout << f
[m] << endl;
}
}

int main()
{
run();
return 0;
}


4000
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: