这道题本身倒是不难,但是卡时间卡的太严重了。对于t是10^4次方查找,我们递归将C(n,m)求出来,并用一个数组记忆化保存,然后对于每次查询,我们循环遍历就可以了~
#include <iostream>
#include <cstring>
using namespace std;
#define Max 2005
typedef long long int ll;
ll C[Max][Max];
ll get_c(int n,int m);
int main()
{
int res=0;
memset(C,0, sizeof(C));
int t,k,n,m;
cin>>t>>k;
for(int e=1;e<=t;e++)
{
res=0;
cin>>n>>m;
for(int i=0;i<=n;i++)
{
for(int j=0;j<=min(m,i);j++)
{
if(!(get_c(i,j)%k))
{
res++;
}
}
}
cout<<res<<endl;
}
return 0;
}
ll get_c(int n,int m)
{
if(C[n][m])
{
return C[n][m];
}
else if(n==m||m==0)
{
return C[n][m]=1;
}
else
{
return C[n][m]=get_c(n-1,m-1)+get_c(n-1,m);
}
}
蜜汁自信,果然。后面有几个数据溢出WA掉了,有几个数据超时了。
对于WA掉的数据,很好处理,只需要先办法使得他不溢出就行了呗~
防止溢出我们可以用取余的办法,这个办法可以防止溢出并且使得最后的结果不会变~
如图所示,我们将所有的Cn,m (n,m<=2000) 全部算出来
C[0][0]=1;
C[1][0]=C[1][1]=1;
for(int i=2;i<=2000;i++)
{
C[i][0]=1;
for(int j=1;j<=i;j++)
{
C[i][j]=C[i-1][j-1]+C[i-1][j];
}
}
C[i][j]即使是long long还是避免不了溢出,所以我们取余想办法,取余可以但是不能瞎取余
首先呢,对于取余运算有一个公式:(a+b)%r=(a%r+b%r)%r
我们设C10 =a C11=b, C21就等于a+b C20=c C22=d
C31 =a+b+c C32=a+b+d
我们可以在计算完Ci,j以后将其直接%k,如果结果是0,那么就用一个数组flag[i][j]=1,来标记,表示Ci,j可以整除k。
但是要如何取模,并证明正确性呢?看到上面那个图:
C31%k=(a+b+c)%k;
C21%k=(a+b)%k;
C20%k=c%k
(c%k+(a+b)%k)%k==(a+b+c)%k
左边等于右边,所以这样取模结果成立,代码如下~
for(int i=2;i<=2000;i++)
{
C[i][0]=1;
for(int j=1;j<=i;j++)
{
C[i][j]=(C[i-1][j-1]+C[i-1][j])%k;
if(!C[i][j])
{
flag[i][j]=1;
}
}
}
这样就可以避免溢出了~
虽然这样可以避免溢出但是避免不了超时,这时候就要用到浅醉和了~
如果我么输入 n=3 m=3
就是这样~
我们要找的就是
按照我们上面那个算法取模计算的话,我们是统计这些变量有多少个值为0,就是要输出的结果了~
然鹅,t<=10^4 这么多操作,如果每次都去统计恐怕力不从心了。结果可想而知,肯定是会T掉2个点,这个题的测试点有20个。。。。。
所以这时候要轮到我们前缀和登场了~
二维数组前缀和直接用这样一个递归公式
a[i][j]=a[i][j]+a[i-1][j]+a[i][j-1]-a[i-1][j-1]
a[i][j]表示左上角是(0,0),右下角是(i,j)的正方形or长方形区域里面的值的和
如图:
a[2][2]表示图中正方形里面值的和
上面那个前缀和递归公式不太理解的同学建议取看看其他dalao的博客~
比如这个dalao的博客
我们在计算a[2]2[] 的时候,就是a[2][2]=a[2][2]+a[2][1]+a[1][2]-a[1][1]
因为我们在遍历这个杨辉三角的时候压根不会遇到a[1][2] ,所以此时a[1][2]=0,所以我们依次计算出。。。a[1][0] a[1][1] 以后还要将a[1][2]=a[1][1]
也就是说我们每次内层循环结束以后一定要搞个复制操作将a[i][i+1]=a[i][i]
下面是ACcode~
#include <iostream>
#include <cstring>
using namespace std;
#define Max 2005
int a[Max][Max];
int C[Max][Max];
int t,k,n,m;
int main()
{
C[0][0]=1;
C[1][0]=C[1][1]=1;
cin>>t>>k;
memset(a,0, sizeof(a));
if(k==1)
{
a[0][0]=1;
a[1][0]=2;
a[1][1]=3;
a[1][2]=3;
}
for(int i=2;i<=2000;i++)
{
C[i][0]=1;
if(k==1)
{
a[i][0]=a[i-1][0]+1;
}
for(int j=1;j<=i;j++)
{
C[i][j]=(C[i-1][j-1]+C[i-1][j])%k;
if(!C[i][j])
{
a[i][j]++;
}
a[i][j]=a[i][j]+a[i-1][j]+a[i][j-1]-a[i-1][j-1];
}
a[i][i+1]=a[i][i];
}
for(int e=1;e<=t;e++)
{
cin>>n>>m;
if(m>n)
{
m=n;
}
cout<<a[n][m]<<endl;
}
return 0;
}