题解:本题主要考查组合数学+前缀和。
简要题意:给
n
,
m
,
k
n,m,k
n,m,k,对于所有的
0
≤
i
≤
n
,
0
≤
j
≤
m
i
n
(
i
,
m
)
0≤i≤n,0≤j≤min(i,m)
0≤i≤n,0≤j≤min(i,m),求有多少对
(
i
,
j
)
(i,j)
(i,j)满足
C
i
j
C_i^j
Cij为
k
k
k的倍数。
1.组合数学:杨辉三角第
i
i
i行第
j
j
j列的值就是
C
i
j
C_i^j
Cij的值,所以我们把杨辉三角打出来。
公式:
c
[
i
]
[
j
]
=
c
[
i
−
1
]
[
j
]
+
c
[
i
−
1
]
[
j
−
1
]
c[i][j]=c[i-1][j]+c[i-1][j-1]
c[i][j]=c[i−1][j]+c[i−1][j−1]
2.前缀和:如果我们不加优化就只得90分(不要问我怎么知道),所以要用前缀和优化。优化很巧妙,用二维前缀和,每一次查询就由
O
(
n
)
O(n)
O(n)降到
O
(
1
)
O(1)
O(1)。
代码如下:
#include<iostream>
#include<algorithm>
#include<cstring>
int c[2333][2333],s[2333][2333];
int n,m,k,t;
using namespace std;
void YH()
{
c[0][0]=1;
c[1][0]=c[1][1]=1;
for(int i=0;i<=2323;i++)c[i][0]=1;
for(int i=2;i<=2323;i++)
for(int j=1;j<=i;j++)
c[i][j]=(c[i-1][j]+c[i-1][j-1])%k;//杨辉三角
for(int i=2;i<=2323;i++)
{
for(int j=1;j<=i;j++)
{
s[i][j]=s[i-1][j]+s[i][j-1]-s[i-1][j-1];//前缀和
if(c[i][j]==0)s[i][j]++;
}
s[i][i+1]=s[i][i];
}
}
int main()
{
cin>>t>>k;
YH();
for(int i=1;i<=t;i++)
{
cin>>n>>m;
if(m>n)cout<<s[n][n]<<endl;
else cout<<s[n][m]<<endl;
}
return 0;
}