- 【清华集训2016】组合数问题
- 其实卢卡斯定理是一个(K)进制拆分的过程。
- 考虑每一次卢卡斯迭代的过程,实际上就是把最后一位数单独拿出来算,然后把最后一位去掉。
- 那么如果我们把两个位置上的数都拆分的话,那么就相当于每一个位置上的组合数乘起来。
- 即
[C_{p,q}=prod_{ileq p,jleq q} C_{i,j}
]
- 所以问题就变成了,有多少个(ileq n,jleq m)的数对((i,j)),满足在(K)进制拆分下的某一位数(i<j)。
- 这个就比较套路了,直接数位(dp)即可,设(f_{i,01,01,01})表示当前在(i)位,是否满足要求,(i)的填法是否危险,(j)的填法是否危险,记忆化即可。
- 复杂度(O(8*log_kn))
#include<bits/stdc++.h>
#define R register int
#define ll long long
using namespace std;
const int N=100011;
const int mod=1e9+7;
const int inv=500000004;
int t,K,l1,l2,Dc,O1[N],O2[N],C[201][201];
int f[2][2][2][N];
ll n,m;
int gi(){
R x=0,k=1;char c=getchar();
while(c!='-'&&(c<'0'||c>'9'))c=getchar();
if(c=='-')k=-1,c=getchar();
while(c<='9'&&c>='0')x=(x<<3)+(x<<1)+c-'0',c=getchar();
return x*k;
}
void add(R &x,R y){x=(x+y>=mod?x+y-mod:x+y);}
int Dfs(R is,R q,R p,R i){
if(!i)return is;
if(f[is][p][q][i]!=-1)return f[is][p][q][i];
R &nw=f[is][p][q][i],lm1=(q?(K-1):O1[i]),lm2=(p?(K-1):O2[i]);
nw=0;
for(R j=0;j<=lm1;++j)
for(R k=0;k<=lm2;++k)
add(nw,Dfs(is|(j<k),q|(j<lm1),p|(k<lm2),i-1));
return f[is][p][q][i];
}
void sol(){
scanf("%lld %lld",&n,&m);
l1=l2=0,m=min(n,m),Dc=m%mod;
memset(f,-1,sizeof(f));
while(n)O1[++l1]=(n%K),n/=K;
while(m)O2[++l2]=(m%K),m/=K;
while(l2<=l1)O2[++l2]=0;
R ans=Dfs(0,0,0,l1);
add(ans,mod-(1ll*(Dc+1)*Dc%mod*inv%mod));
printf("%d
",ans);
}
int main(){
t=gi(),K=gi();
while(t--)sol();
return 0;
}