题目链接:点击这里
经验:本题mod1007,而n已经到了2000,不能保证互质,所以说什么暴力阶乘啊,乘法逆元啊,费马小定理啊,lucas啊,都不要想了,不如直接递推预处理。
思路:此题需要一点转换的思维,题目中有m个循环,并且我们可以发现后面的循环中的指针一定比前面一个大,并且会出现从n个数中取m个数的所有可能性(前面的数比后面小就行,比如1,3,4),那么问题可以转换为从n个数中取m个数的种类数
也就是说从1->n这n个数中随机选择m个数,只需要将它们按大小顺序填到每一层循环里就可以啦,答案就是C(n,m)。
思路就是这个样子,然后就是计算组合数,可以用递推公式
C(n,m)=C(n-1,m-1) + C(n-1,m)
至于这个公式,高中的时候将排列组合就听老师说过了,不过当时算题都是手算,根本不理解递推公式的重要性,就认为这公式没吉儿用处。。。
公式推导:
等式左边表示从n个元素中选取m个元素,而等式右边表示这一个过程的另一种实现方法:任意选择n中的某个备选元素为特殊元素,从n中选m个元素可以由此特殊元素的分成两类情况,即m个被选择元素包含了特殊元素和m个被选择元素不包含该特殊元素。
然后就是代码了:
#include<bits/stdc++.h>
using namespace std;
int a[2100][2100] = {0};
void inti(void){
a[0][0] = 1;
for(int i=1;i<2100;i++){ //C(i,j)
a[i][0] = 1;
for(int j=1;j<=i;j++){
a[i][j] = (a[i-1][j-1] + a[i-1][j])%1007;
}
}
}
int main(void){
int t,n,m;
cin>>t;
inti();
while(t--){
cin>>m>>n;
cout<<a[n][m]<<endl;
}
return 0;
}
还有就是洋神说的lucas,之前都没听说过,还没看,待补。