题解
本题主要知识点如下:
1.组合数的递推公式:c(m,n)=c(m-1,n-1)+c(m-1,n)
2.用前缀和优化查询:将复杂度从O(n)降到O(1)
前缀和:
1)一维数组的前缀和:sum[i]=a[0]+…+a[i];
2)二维数组的前缀和;sum[i][j]为左上角(0,0)到右下角(i,j)矩形中的所有a[ ][ ]的和
那么前缀和sum[i][j]就等于蓝的矩阵sum[i-1][j]加上绿的矩阵sum[i][j-1],再减去重叠面积sum[i-1][j-1],最后加上小方块
即:sum[i][j] = sum[i][j - 1] + sum[i - 1][j] - sum[i - 1][j - 1] + a[i][j] (记忆口诀:上加左 减左上 加自己)
代码:
#include <iostream>
using namespace std;
typedef long long ll;
int c[2005][2005];
int ans[2005][2005];
int n,m,k;
void C(int n)///打表求c[i][j],ans[i][j]一起求出来
{
for(int i=0;i<=n;i++) c[i][i]=c[i][0]=1;
for(int i=2; i<=n; i++)
{
for(int j=1; j<=i; j++)///j要=i,因为ans[i][i]也要求出值
{
c[i][j]=(c[i-1][j-1]+c[i-1][j])%k;
ans[i][j]=ans[i][j-1]+ans[i-1][j]-ans[i-1][j-1];///求前缀和,加自己a[i][j],放在下面的if里了
///只有当c[i][j]==0,a[i][j]才=1,即c[i][j]才需要加一
if(c[i][j]==0) ans[i][j]++;///若整除,则ans++
}
ans[i][i+1]=ans[i][i];
///继承,因为ans[i+1][j+1]上方的值(当成矩阵来看)应为ans[i][j+1]=ans[i][i]
///没赋值时为0
}
//cout<<c[7][2];
}
int main()
{
int T;cin>>T>>k;
C(2000);
while(T--)
{
int n,m;
cin>>n>>m;
if(m>n) cout<<ans[n][n]<<endl;
else cout<<ans[n][m]<<endl;
}
return 0;
}