观察题目,显然没办法直接判断是否 k \mid \begin{pmatrix} i \\ j \end{pmatrix},观察是否能转化。根据组合数公式可以转化为 k \mid \cfrac{i!}{j!(i - j)!},因为 k 为质数,那么想要能整除,那么分子因数 k 的个数一定要大于分母因数 k 的个数,这样最后才能留下至少一个 k 在结果里。如果定义 [n]_k 为在 n 的质因数分解中包含 k 的个数,例如 [50]_5 = 2,因为 50 = 5 \times 5 \times 2。那么上述条件转化为 [i!]_k > [j!]_k + [(i - j)!]_k。
总算法时间复杂度为 O(Tn^2\log_k n),其中 O(\log_k n) 为单次将 n 和 m 转化为 k 进制的时间复杂度数量级,其中因为枚举 flag 的值对于每一个 flag 只有两种情况,因此作为常数省略。总空间复杂度约为 O(\log_k n),flag 维度所占空间同理作为常数省略。
别忘记取模,千万记得每一个都取模,多测别忘记清空,注意数据范围。注意当 n < m 时第二种情况不存在,且应以 \min(n,m) 代替 m 来防止 n < m 的情况导致累加过度。
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define endl "\n"
const int mod = 1e9 + 7;
int t,k;
int n,m;
int tot;
int res,ans;
int a[105],b[105];//n和m的k进制拆分
int dp[105][2][2];
void init(){
memset(a,0,sizeof(a));
memset(b,0,sizeof(b));
memset(dp,0,sizeof(dp));
tot = 0;
res = ans = 0;
m = min(n,m);//防止n < m
int numa = n,numb = m;
while(numa != 0){
a[++ tot] = numa % k;
b[tot] = numb % k;
numa /= k;
numb /= k;
}
dp[tot][1][1] = 1;
}
signed main(){
cin >> t >> k;
while(t --){
cin >> n >> m;
init();
for(int i=tot-1;i>=0;i--){
for(int flag1=0;flag1<=1;flag1++){
for(int flag2=0;flag2<=1;flag2++){
if(!dp[i + 1][flag1][flag2]) continue;//上一个情况不成立
//枚举放什么
for(int l=0;l<k;l++){
if(flag1 && l > a[i + 1]) continue;//如果已经取等,这一位还更大,那么说明如果取到这个就比n_i大了,因此不合法
for(int r=0;r<=l;r++){//要满足不借位就要满足填的不比i填的大
if(flag2 && r > b[i + 1]) continue;//如果已经取等,这一位还更大,那么说明如果取到这个就比m_i大了,因此不合法
dp[i][flag1 && (l == a[i + 1])][flag2 && (r == b[i + 1])] += dp[i + 1][flag1][flag2];
dp[i][flag1 && (l == a[i + 1])][flag2 && (r == b[i + 1])] %= mod;
}
}
}
}
}
res = (m + 1) % mod * ((m + 2) % mod) % mod * ((mod + 1) / 2) % mod + (n - m) % mod * ((m + 1) % mod) % mod;
res %= mod;
for(int flag1=0;flag1<=1;flag1++){
for(int flag2=0;flag2<=1;flag2++){
ans += dp[0][flag1][flag2];
ans %= mod;
}
}
cout << ((res - ans) % mod + mod) % mod << endl;
}
return 0;
}