题目
题意概要
求 中多少个数满足,若非零整数 在其十进制表示下出现过,则 为其因数。
数据范围与约定
数据组数 , 。
思路
我最初的想法是,十二维 ,用 表示, 是已经出现过的数字(状压), 是原来的数模 的值,而 表示是否已经小于上界。考虑到第几位,滚动数组优化掉——我往往这么做。你想试试吗?
仔细思考一下,我们并不在乎模每一个已经出现过的数字的余数。因为我们要求它们全部都是零。所以,我们应该考虑 模其最小公倍数的值。
然而不能走一步就模一次,不然最小公倍数发生变化就惨了。比如 ,但是 就不等于 的值了。
然而数字集是很小的。不难发现所有的最小公倍数都是 的因数,所以模 行得通。
得预处理一下所有可能的 。有多少个?可以直接计算: 是有可能出现的质因数分解,所以可能的情况是 个。
发现状态数明显下来了!用 表示,原数模 的结果是 ,最小公倍数为 。
代码
#include <cstdio>
#include <iostream>
#include <vector>
#include <algorithm>
#include <cstring>
#include <cmath>
using namespace std;
inline int readint() {
int a = 0; char c = getchar(), f = 1;
for(; c<'0' or c>'9'; c=getchar())
if(c == '-') f = -f;
for(; '0'<=c and c<='9'; c=getchar())
a = (a<<3)+(a<<1)+(c^48);
return a*f;
}
void writeint(int x){
if(x < 0) putchar('-'), x = -x;
if(x > 9) writeint(x/10);
putchar((x%10)^48);
}
# define MB template < typename T >
MB void getMax(T &a,const T &b){ if(a < b) a = b; }
MB void getMin(T &a,const T &b){ if(b < a) a = b; }
# define FOR(i,n) for(int i=0; i<(n); ++i)
# define LB lower_bound
typedef long long int_;
int getLcm(int a,int b){ return a/__gcd(a,b)*b; }
const int m = 48; // 不同的lcm的数量
const int all = 2520; // 最大的lcm(所有lcm的lcm)
int_ tmp[all][m][2], dp[all][m][2];
int lcm[m] = {1,2,3,4,5,6,7,8,9,10,12,14,15,18,20,
21,24,28,30,35,36,40,42,45,56,60,63,70,72,84,
90,105,120,126,140,168,180,210,252,280,315,360,
420,504,630,840,1260,2520
}; // 打个表就行咯
int __lcm[m][10]; // lcm[i]与j的lcm
int f[all+1]; // O(1)的对应回lcm
int d[19];
int_ work(int_ num){
for(int i=18; ~i; --i)
d[i] = num%10, num /= 10;
static const int siz = all*m<<4;
memset(dp,0,siz), dp[0][0][0] = 1;
for(int x=0; x<19; ++x){
memset(tmp,0,siz);
FOR(now,10) FOR(i,all) FOR(j,m){
int i_ = (i*10+now)%all;
int j_ = __lcm[j][now];
int_ *p = tmp[i_][j_];
if(now <= d[x])
p[now<d[x]] += dp[i][j][0];
p[1] += dp[i][j][1];
}
swap(dp,tmp); // dp = tmp
}
int_ res = 0;
FOR(j,m) for(int i=0; i<all; i+=lcm[j])
res += dp[i][j][1];
return res;
}
int main(){
FOR(i,m) f[lcm[i]] = i;
FOR(i,m) FOR(j,10) if(j != 0)
__lcm[i][j] = f[getLcm(lcm[i],j)];
FOR(i,m) __lcm[i][0] = i; // 不变
for(int T=readint(); T; --T){
int_ x, ans = 0;
cin >> x, ans -= work(x);
cin >> x, ans += work(x+1);
cout << ans << endl;
}
return 0;
}