您的位置:首页 > 其它

解题报告 Noip2016提高组 组合数问题

2017-11-03 14:04 162 查看
Day2 T1

题目大意

告诉你组合数公式,其中n!=1*2*3*4*5*…*n;意思是从n个物体取出m个物体的方案数

现给定n、m、k,问在所有i(1<=i<=n),所有j(1<=j<=min(i,m))的(i,j)满足Cji是k的倍数的个数。

输入样例:

2 5 (两个数,第一个数t表示该数据有t组询问,第二个为k,接下来t行分别为n,m)

4 5

6 7

输出样例:

0

7

数据范围:1<=n,m<=2000,1<=t<=10000,1<=k<=21

数论题,当时做竟然没发现这就是个杨辉三角,就是少了第一列全是1的,真是悲剧。

组合数的递推式就是Cmn=Cm-1n-1+Cmn-1

因为k一开始就固定了,所以预处理2000以内的个数,用前缀和优化优化就可以AC了,当然用二维前缀和似乎能优化到O(1),不麻烦每行一个前缀和到时候O(n)的得出答案也不会超时。

这是一道数论题

首先要知道组合数的一般递推公式,它的递推公式和杨辉三角是一样的

c[i][j]=c[i-1][j-1]+c[i-1][j]

(解释:c[i][j]即为从i件物品中选j件的方案数。如果第i件物品不选,方案数就变为c[i-1][j],如果选第i件物品,方案数就变为c[i-1][j-1],总方案数就为两种情况的方案数之和)

为了不爆long long,每次求出c[i][j]后先模一下k

为了节约时间,进行二维求和,最后直接查找答案

#include<cstdio>
#include<iostream>
#include<cstring>
#include<cmath>
using namespace std;
int a[2005][2005],ji[2005][2005];
int n,m,ans,t,k;
int main(){
scanf("%d%d",&t,&k);
memset(a,0,sizeof(a));
a[1][1]=1%k;
if (a[1][1]==0) ji[1][1]++;
for (int j=2;j<=2001;j++)
for (int q=1;q<=min(j,2001);q++){
if (q==1) a[j][q]=(a[j-1][q]+1)%k;
else a[j][q]=(a[j-1][q]+a[j-1][q-1])%k;
if (a[j][q]==0) ji[j][q]=ji[j][q-1]+1; else ji[j][q]=ji[j][q-1];
}
for (int i=1;i<=t;i++){
scanf("%d%d",&n,&m);
ans=0;
for (int j=1;j<=n;j++)
ans+=ji[j][min(m,j)];
printf("%d\n",ans);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: