【问题描述】
组合数表示的是从n个物品中选出m个物品的方案数。举个例子,从(1,2,3) 三个物品中选择两个物品可以有(1,2),(1,3),(2,3)这三种选择方法。根据组合数的定义,我们可以给出计算组合数
的一般公式:
其中n! = 1 × 2 × … × n。
小葱想知道如果给定n,m和k,对于所有的0 <= i <= n,0 <= j <= min(i,m)有多少对 (i,j)满足是k的倍数。
【输入格式】
第一行有两个整数t,k,其中t代表该测试点总共有多少组测试数据,k的意义见 【问题描述】。
接下来t行每行两个整数n,m,其中n,m的意义见【问题描述】。
【输出格式】
t行,每行一个整数代表所有的0 <= i <= n,0 <= j <= min(i,m)有多少对 (i,j)满足是k的倍数。
【输入输出样例】
输入样例#1:
1 2
3 3
输出样例#1:
1
输入样例#2:
2 5
4 5
6 7
输出样例#2:
0
7
【说明】
【样例1说明】
在所有可能的情况中,只有是2的倍数。
【子任务】
【题解】
考试的时候除了暴力没有任何思路,所以就直接约分,拿了40分。
#include<iostream>
#include<cstdio>
using namespace std;
int main()
{
int T;
int M,m,n;
long long C;
int ans;
scanf("%d%d",&T,&M);
while(T--)
{
ans=0;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
for(int j=1;j<=min(i,m);j++)
{
if(i==j) continue;
else
{
long long q=1,w=1;
for(int k=i;k>=j+1;k--)
{
q*=k;
}
for(int t=(i-j);t>=1;t--)
{
w*=t;
}
C=q/w;
if(C%M==0) ans++;
}
}
cout<<ans<<endl;
}
return 0;
}
正解:数论——杨辉三角形(然而考试的时候并无思路)
对于每一个(n,m)
n m C
1 1 1
2 1 2
2 2 1
3 1 3
3 2 3
3 3 1
4 1 4
4 2 6
4 3 4
4 4 1
…… …… ……
再加一个前缀和处理即可ac
#include<iostream>
#include<cstdio>
int s[2005][2005];
int ans[2005][2005];
using namespace std;
int main()
{
int T,k;
scanf("%d%d",&T,&k);
for(int i=0;i<2001;i++)
{
s[i][0]=0;
s[0][i]=1;
}
for(int i=1;i<2001;i++)
for(int j=1;j<2001;j++)
s[i][j]=(s[i][j-1]+s[i-1][j-1])%k;//杨辉三角
for(int i=1;i<2001;i++)
{
for(int j=1;j<2001;j++)//前缀和
{
ans[j][i]=ans[j-1][i]+ans[j][i-1]-ans[j-1][i-1];
if(s[j][i]==0)
ans[j][i]++;
}
ans[i][i]=ans[i-1][i]; //处理边界
if(s[i][i]=0) ans[i][i]++;
}
while(T--)
{
int n,m;
scanf("%d%d",&n,&m);
while(m>n) m--;
printf("%d\n",ans[m][n]);
}
return 0;
}