内个……我还没有计蒜客的账号,就先不给题目传送门了……
看到数据范围不正常,就想到了数位DP。
定义
f[i][j]
表示从高到底DP到第
i
位,第
当我们已知 f[i−1] 时,可以用两次类似于埃氏筛的方法求出 f[i] ,时间复杂度为 O(plog2plogpr)=O(plog2r) 。
附上AC代码:
#include <cstdio>
using namespace std;
typedef long long ll;
const int N=1e5+10;
int t,mod,p[N],miu[N],num,len,a[70];
ll f[70][N],l,r;
bool b[N];
inline void get(int n){
miu[1]=1;
for (int i=2; i<=n; ++i){
if (!b[i]) p[++num]=i,miu[i]=-1;
for (int j=1; j<=num&&p[j]*i<=n; ++j){
b[p[j]*i]=1,miu[p[j]*i]=-miu[i];
if (i%p[j]==0) {miu[p[j]*i]=0;break;}
}
}
return;
}
inline void change(ll x){
len=0;do a[++len]=x%mod,x/=mod; while (x);
return;
}
inline int gcd(int a,int b){return !b?a:gcd(b,a%b);}
inline ll so(int x,int pre,bool b){
if (x==1) return 1ll;
if (!b&&f[x][pre]) return f[x][pre];
ll ans=0,lim=b?a[x-1]:mod-1;
for (int i=1; i<=lim; ++i) if (gcd(pre,i)==1) ans+=so(x-1,i,b&&(i==lim));
if (!b) f[x][pre]=ans;
return ans;
}
inline ll calc(ll x){
change(x);ll ans=0;
for (int i=1; i<len; ++i) for (int j=1; j<mod; ++j) ans+=f[i][j];
for (int i=1; i<=a[len]; ++i) ans+=so(len,i,i==a[len]);
return ans;
}
int main(void){
for (get(1e5),scanf("%d",&t); t; --t){
scanf("%lld%lld%d",&l,&r,&mod),change(r);
for (int i=0; i<=len; ++i) for (int j=0; j<mod; ++j) f[i][j]=0;
for (int i=1; i<mod; ++i) f[1][i]=1;
for (int i=2; i<=len; ++i)
for (int d=1; d<mod; ++d){
ll sum=0;
for (int t=d; t<mod; t+=d) sum+=f[i-1][t];
for (int t=d; t<mod; t+=d) f[i][t]+=sum*miu[d];
}
printf("%lld\n",calc(r)-calc(l-1));
}
return 0;
}