题意:
定义一个数字是好的,当且仅当它的各位数字之积与其本身的 \(\gcd>1\)。
\(T\) 次询问,每次给定 \(L\le R\),问 \([L,R]\) 中有多少个数字是好的。
注意:\(\gcd(0,0)\) 不存在。\(\gcd(0,x\gt 0)=x\)。
\(T=10^4,1\le L\le R\le 10^18\)。
分析:
一眼数位dp没问题。相当于问 \(\le x\) 的好数有多少个。
容易想到朴素的 dp:设 \(f(i,m2,m3,m5,m7,mask,0/1)\) 表示从高往低确定了 \(i\) 位,是否已经小于 \(x\),然后当前部分模 \(2,3,5,7\) 的余数分别为 \(m2,m3,m5,m7\),而各位数字包含的质因数集合为 \(mask\)。乘上转移后单次复杂度是 \(9\times 18\times 2^4\times 2\times 210(2\times 3\times 5\times 7)\)。看上去很优秀,\(10^4\) 组数据后 T 飞。
感觉每次都做一遍 dp 没有前途,能不能预处理一些信息,每次询问快速统计。
正整数里,如果带 \(0\) 的那么一定合法(忽略前导零),所以带 \(0\) 的是好统计的。我们仅考虑所有数字都不出现 \(0\) 的情况。
分类讨论:设 \(x\) 的位数为 \(cnt\)。首先我们预处理位数小于 \(cnt\) 的答案,这个容易 dp 预处理然后统计。然后考虑位数 \(=cnt\) 的。
不妨令数 \(\lt cnt\) 而不是 \(\le cnt\),特判一下 \(cnt\) 是否合法即可。
套路地枚举前缀重合部分,和下一位的数字(小于 \(x\) 上该位数字且不为 \(0\))(注意前缀重合部分也不能有 \(0\))。
然后我们更改 dp 状态,\(f(i,m2,m3,m5,m7,mask)\) 是从低往高选 \(i\) 位,\(m2,m3,m5,m7,mask\) 的定义不变。注意到这里的 dp 我们不用再去考虑是否 \(\lt x\) 这件事情了。
设前缀重合部分长度为 \(num\),由于我们知道前缀(还有暴力枚举的下一位)的信息(就是说余数和数码的质因数集合),那么我们可以和 \(dp(cnt-num-1)\) 的信息合并。
如果暴力枚举的话,总的复杂度依旧是很高的...... 我们考虑再把这个东西预处理出来:\(g(i,m2,m3,m5,m7,mask)\) 代表我们还要选择末尾的 \(i\) 位数字,而前缀部分的信息是 \(m2,m3,m5,m7,mask\) 的个数。
考虑计算 \(g\):\(16^2\) 的枚举后 \(i\) 位数字的质因数信息,和最后符合条件(指 \(m=0\) 且质因数集合里它也出现)的质因数,然后此时的信息再用一个 \(h\) 数组预处理...... 处理出来以后直接根据枚举的,后 \(i\) 位质因数集合的 \(size\) 的奇偶进行容斥就好了。
算一下发现预处理计算量大概 \(3e7\),而最后单次询问的计算量就只有 \(18\times 9\) 了,所以 \(T\) 其实可以开到 \(5\times 10^5\)......
//https://codeforces.com/gym/103462/problem/D
#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define per(i,a,b) for(int i=(a);i>=(b);i--)
#define op(x) ((x&1)?x+1:x-1)
#define odd(x) (x&1)
#define even(x) (!odd(x))
#define lc(x) (x<<1)
#define rc(x) (lc(x)|1)
#define lowbit(x) (x&-x)
#define mp(x,y) make_pair(x,y)
typedef long long ll;
typedef unsigned long long ull;
typedef double db;
using namespace std;
const int MAXN=20;
ll f[MAXN][2][3][5][7][16],g[MAXN][2][3][5][7][16],h[MAXN][3][4][6][8][16];
ll dp[MAXN];
ll power[10][MAXN],power9[MAXN],pcnt[16];
int state[10]={15,0,1,2,1,4,3,8,1,2};
int N(int x,int mod){
if(!x)return x;
return mod-x;
}
int check(int m2,int m3,int m5,int m7,int mask){
if(!m2 && (mask&1))return 1;
if(!m3 && (mask&2))return 1;
if(!m5 && (mask&4))return 1;
if(!m7 && (mask&8))return 1;
return 0;
}
void pre(){
rep(i,1,15)pcnt[i]=pcnt[i^lowbit(i)]+1;
power[0][0]=1;rep(i,1,19)power[0][i]=power[0][i-1]*10;
power9[0]=1;rep(i,1,19)power9[i]=power9[i-1]*9;
rep(mod,1,9){power[mod][0]=1;rep(i,1,19)power[mod][i]=power[mod][i-1]*10%mod;}
f[0][0][0][0][0][0]=1;
rep(i,0,18)rep(m2,0,1)rep(m3,0,2)rep(m5,0,4)rep(m7,0,6)rep(mask,0,15)rep(d,1,9){
f[i+1][(m2+d*power[2][i])%2][(m3+d*power[3][i])%3][(m5+d*power[5][i])%5]\
[(m7+d*power[7][i])%7][mask|state[d]]+=f[i][m2][m3][m5][m7][mask];
}
rep(i,0,18)rep(m2,0,1)rep(m3,0,2)rep(m5,0,4)rep(m7,0,6)rep(mask,0,15)if(check(m2,m3,m5,m7,mask))dp[i]+=f[i][m2][m3][m5][m7][mask];
rep(i,0,18)rep(m2,0,2)rep(m3,0,3)rep(m5,0,5)rep(m7,0,7)rep(mask,0,15){
rep(r2,0,1){if(m2<2 && m2!=r2)continue;
rep(r3,0,2){if(m3<3 && m3!=r3)continue;
rep(r5,0,4){if(m5<5 && m5!=r5)continue;
rep(r7,0,6){if(m7<7 && m7!=r7)continue;
h[i][m2][m3][m5][m7][mask]+=f[i][r2][r3][r5][r7][mask];
}
}
}
}
}
rep(i,0,18)rep(m2,0,1)rep(m3,0,2)rep(m5,0,4)rep(m7,0,6)rep(mask,0,15){
ll& ret=g[i][m2][m3][m5][m7][mask];
rep(mask2,0,15){
int S=mask|mask2;
for(int mask3=S;mask3;mask3=(mask3-1)&S){
ll r2=(mask3&1)?(N(m2,2)):(2),
r3=(mask3&2)?(N(m3,3)):(3),
r5=(mask3&4)?(N(m5,5)):(5),
r7=(mask3&8)?(N(m7,7)):(7);
if(odd(pcnt[mask3]))ret+=h[i][r2][r3][r5][r7][mask2];
else ret-=h[i][r2][r3][r5][r7][mask2];
}
}
}
}
ll T,l,r;
int d[30],cnt;
ll solve(ll x){
if(!x)return 0;
ll ans=0,num=x;cnt=0;
while(num){
d[++cnt]=num%10;
num/=10;
}
rep(len,1,cnt-2){ans+=9ll*(power[0][len]-power9[len]);}
reverse(d+1,d+1+cnt);
ll m2=0,m3=0,m5=0,m7=0,mask=0;
rep(i,1,cnt-1)ans+=dp[i];
rep(i,1,cnt){
rep(nxt,1,d[i]-1){
int r2,r3,r5,r7;
r2=(m2+power[2][cnt-i]*nxt)%2;
r3=(m3+power[3][cnt-i]*nxt)%3;
r5=(m5+power[5][cnt-i]*nxt)%5;
r7=(m7+power[7][cnt-i]*nxt)%7;
int masks=mask|state[nxt];
if(i==cnt){ans+=check(r2,r3,r5,r7,masks);}
else ans+=g[cnt-i][r2][r3][r5][r7][masks];
if(i==1){
int res=0;
rep(j,1,9){
if(__gcd(nxt*j,nxt*10+j)>1)res++;
}
}
ans+=power[0][cnt-i]-power9[cnt-i];
}
if(d[i]==0){
ll val=0;
rep(j,i+1,cnt)val=val*10+d[j];
ans+=val+1;
goto END;
}else{
if(i!=1)ans+=power[0][cnt-i];
}
m2=(m2+power[2][cnt-i]*d[i])%2;
m3=(m3+power[3][cnt-i]*d[i])%3;
m5=(m5+power[5][cnt-i]*d[i])%5;
m7=(m7+power[7][cnt-i]*d[i])%7;
mask|=state[d[i]];
}
ans+=check(m2,m3,m5,m7,mask);
END:;return ans;
}
void solve(){
cin>>l>>r;
cout<<solve(r)-solve(l-1)<<endl;
}
int main(){
pre();
cin>>T;
while(T--)solve();
return 0;
}