题面传送门
对于这道题,其实题目很简单,暴力很好打。
想法
1
1
1:对于每组输入数据,暴力判断两重循环枚举,一重循环计算组合数。时间复杂度
O
(
t
n
3
)
O(tn^3)
O(tn3),大概
30
30
30分
想法
2
2
2:把每组数的组合存下来,直接调用。时间复杂度
O
(
t
n
2
)
O(tn^2)
O(tn2)大概
35
35
35分。
想法
3
3
3:既然直接算组合数会爆精度,那么在计算组合数是就不能有除法和减法,只有加法和乘法满足
(
a
%
c
)
(
b
%
c
)
%
c
=
(
a
b
)
%
c
(a\%c)(b\%c)\%c=(ab)\%c
(a%c)(b%c)%c=(ab)%c与
(
a
%
c
+
b
%
c
)
%
c
=
(
a
+
b
)
%
c
(a\%c+b\%c)\%c=(a+b)\%c
(a%c+b%c)%c=(a+b)%c.然后我们自然想起杨辉三角,这是一个求组合数的东西,递推公式为
f
i
,
j
=
f
i
−
1
,
j
+
f
+
i
−
1
,
j
−
1
f_{i,j}=f_{i-1,j}+f+{i-1,j-1}
fi,j=fi−1,j+f+i−1,j−1,所以我们可以递推出来,一遍递推一遍%k,对于每组数据递推一遍,若
%
k
=
=
0
\%k==0
%k==0即有一个答案。大概有
70
70
70分。
想法
4
4
4:因为杨辉三角是一样的,所以可以先递推出来。
递推出来有什么用啊,还不是要
O
(
n
2
)
O(n^2)
O(n2)查询。
这可是提高组原题,提高组不考数据结构考什么。
那么有一种数据结构,修改很麻烦,但查询
O
(
1
)
O(1)
O(1)。
所以我们可以用前缀和过掉这一题。
关于二维前缀和之前的题解已经有讲过,不再赘述。
代码实现:
#include<cstdio>
#define min(a,b) ((a)<(b)?(a):(b))
using namespace std;
int n,m,x,y,f[2039][2039],s[2039][2039],ans,tot,pus;
int main(){
register int i,j;
scanf("%d%d",&n,&m);
f[1][0]=f[1][1]=1;
for(i=2;i<=2000;i++){
f[i][0]=1;
for(j=1;j<=i;j++){
f[i][j]=(f[i-1][j]+f[i-1][j-1])%m;
if(!f[i][j]) s[i][j]=1;
s[i][j]+=s[i-1][j]+s[i][j-1]-s[i-1][j-1];
}
s[i][i+1]=s[i][i];
}
for(i=1;i<=n;i++){
scanf("%d%d",&x,&y);
printf("%d\n",s[x][min(x,y)]);
}
return 0;
}
顺带提一下,当
y
>
x
y>x
y>x的时候解为
s
x
,
x
s_{x,x}
sx,x,因为在
x
x
x个数中选
y
y
y个数一定选不出来。
评测记录
然而我们发现每次都要近似
2000
×
2000
2000\times2000
2000×2000的循环太浪费时间,有些时候没必要递推这么多,所以我们可以记录最大的
n
n
n,并按此递推(真心怕出题人毒瘤)
代码实现:
#include<cstdio>
#define min(a,b) ((a)<(b)?(a):(b))
#define max(a,b) ((a)>(b)?(a):(b))
using namespace std;
int n,m,x[10039],y[10039],f[2039][2039],s[2039][2039],ans,tot,pus;
int main(){
register int i,j;
scanf("%d%d",&n,&m);
for(i=1;i<=n;i++) scanf("%d%d",&x[i],&y[i]),ans=max(x[i],ans);
f[1][0]=f[1][1]=1;
for(i=2;i<=ans;i++){
f[i][0]=1;
for(j=1;j<=i;j++){
f[i][j]=(f[i-1][j]+f[i-1][j-1])%m;
if(!f[i][j]) s[i][j]=1;
s[i][j]+=s[i-1][j]+s[i][j-1]-s[i-1][j-1];
}
s[i][i+1]=s[i][i];
}
for(i=1;i<=n;i++)printf("%d\n",s[x[i]][min(x[i],y[i])]);
return 0;
}