Description
组合数C(n,m)表示的是从n个物品中选出m个物品的方案数。举个例子,从(1,2,3)三个物品中选择两个物品可以有(
1,2),(1,3),(2,3)这三种选择方法。根据组合数的定义,我们可以给出计算组合数C(n,m)的一般公式:
C(n,m)=n!/m!*(n?m)!
其中n!=1×2×?×n。(额外的,当n=0时,n!=1)
小葱想知道如果给定n,m和k,对于所有的0≤i≤n,0≤j≤min(i,m)有多少对(i,j)满足C(i,j)是k的倍数。
Input
第一行有两个整数t,k,其中t代表该测试点总共有多少组测试数据,k的意义见。
接下来t行每行两个整数n,m,其中n,m的意义见。
Output
t行,每行一个整数代表所有的0≤i≤n,0≤j≤min(i,m)中有多少对(i,j))满足C(i,j)是k的倍数
答案对10^9+7取模。
由lucas定理的推论,C(i,j)不是k的倍数当且仅当k进制下i的每一位分别>=j
先补上前导零使n,m位数相同从高位到低位进行数位dp,f[i][0/1][0/1]表示当前已决策高于第i位的部分,已决策部分是否与n,m相等,满足条件的方案数。
#include<cstdio> #include<cstring> const int P=1000000007; typedef long long i64; int T,k,ns[107],ms[107],f[107][2][2]; i64 n,m; int min(int a,int b){return a<b?a:b;} int max(int a,int b){return a>b?a:b;} i64 S(i64 x){return x%=P,x*(x+1)/2%P;} i64 cal(i64 a,i64 b){ if(a<b)b=a; return (S(a)-S(a-b))%P; } i64 s[107][107]; int main(){ scanf("%d%d",&T,&k); for(int i=0;i<=k;++i) for(int j=0;j<=k;++j)s[i][j]=cal(i,j); for(;T;--T){ scanf("%lld%lld",&n,&m); if(n<m)m=n; int ans=cal(n+1,m+1); int np=0,mp=0; while(n)ns[++np]=n%k,n/=k; while(m)ms[++mp]=m%k,m/=k; while(mp<np)ms[++mp]=0; memset(f,0,sizeof(f)); f[np][1][1]=1; for(int i=np;i;--i){ int(*F)[2]=f[i],(*G)[2]=f[i-1]; int vn=ns[i],vm=ms[i]; G[1][1]=F[1][1]*(vn>=vm); G[1][0]=(F[1][0]*i64(vn+1)+F[1][1]*(i64)min(vn+1,vm))%P; G[0][1]=(F[0][1]*i64(k-vm)+F[1][1]*(i64)max(vn-vm,0))%P; G[0][0]=(F[0][0]*s[k][k]+F[0][1]*s[k][vm]+F[1][0]*s[vn][k]+F[1][1]*s[vn][vm])%P; } for(int a=0;a<2;++a) for(int b=0;b<2;++b) ans=(ans-f[0][a][b])%P; printf("%d\n",(ans+P)%P); } return 0; }