bzoj2757: [SCOI2012]Blinker的仰慕者
Description
Blinker 有非常多的仰慕者,他给每个仰慕者一个正整数编号。而且这些编号还隐藏着特殊的意义,即编号的各位数字之积表示这名仰慕者对Blinker的重要度。 现在Blinker想知道编号介于某两个值A,B之间,且重要度为某个定值K的仰慕者编号和。
Input
输入的第一行是一个整数N,表示Blinker想知道的信息个数。
接下来的N行,每行有三个数,A,B,K。表示 Blinker想知道编号介于A和B之间的,
重要度为K的仰慕者的编号和。
Output
输出N行,每行输出介于A和B之间,重要度为 K的仰慕者编号和。结果可能很大,
模上20120427。
Sample Input
3
1 14 4
1 30 4
10 60 5
0<=K<=10^18
Sample Output
18
40
66
【样例解释】
第一组样例中,在 1到14之间各位数字之积等于 4的有 4和 14,故编号和为18。
HINT
【数据范围】
对于20%的数据,保证: 2<=A<=B<=1000000000,1<=N<=30
对于50%的数据,保证:2<=A<=B<=1000000000000000000,1<=N<=30
对于100%的数据,保证: 2<=A<=B<=1000000000000000000,1<=N<=5000
分析
一道很好的数位Dp
可以预计到,乘积的组合应该不多,经计算只有4.4e4种
所以如果是问个数的话就是一个很裸的数位Dp,开个f[i][j]表示第i位,乘积为j的个数。
但是如果是问和的话就要多搞一下,开个g[i][j]表示第i位,乘积为j的和。
前两个东西很好Dp出来,具体看代码。
然后最后统计答案要一些奇技淫巧。
如果确定了一个数的前缀Pr,那么生成的方案数是
Pr∗f[i][K]∗10i+g[i][K]
P
r
∗
f
[
i
]
[
K
]
∗
10
i
+
g
[
i
]
[
K
]
然后Dp要有一个小技巧,前面的f,g递推一下,统计答案用搜索即可。
因为多组询问嘛。
复杂度很妥。
代码
#include<cstdio>
#include<map>
typedef long long LL;
std::map<LL, int>m;
const int N = 5e4, P = 20120427;
int pw[21], bit[21], g[21][N], f[21][N], af[21], ag[21], tp, L;
long long K, st[N];
void Up(int &a, int b) {a += b; if(a >= P) a -= P;}
void Pre() {
pw[0] = 1; for(int i = 1;i <= 20; ++i) pw[i] = pw[i - 1] * 10 % P;
for(int i = 0;i <= 9; ++i) {
m[i] = ++tp; st[++L] = i;
f[1][tp] = 1; g[1][tp] = i;
}
for(int i = 1, nL = L;i <= 18; ++i, L = nL)
for(int j = 1;j <= L; ++j) {
LL x = st[j]; int pr = m[x];
for(int k = 0;k <= 9; ++k) {
if(!m[x * k]) st[++nL] = x * k, m[x * k] = ++tp;
int nx = m[x * k]; Up(f[i + 1][nx], f[i][pr]);
Up(g[i + 1][nx], (1LL * f[i][pr] * pw[i] * k + g[i][pr]) % P);
}
}
for(int i = 1;i < 20; ++i)
for(int j = 1;j <= L; ++j)
Up(af[i], f[i][j]), Up(ag[i], g[i][j]);
}
int Dfs0(int i, int s, bool Z, bool e, bool z) {
if(!i) return Z ? s : 0;
if(!e && !z) {
int A = !Z ? f[i][1] : af[i], B = !Z ? g[i][1] : ag[i];
return (1LL * s * A % P * pw[i] + B) % P;
}
int end = e ? bit[i] : 9, r = 0;
for(int v = 0;v <= end; ++v)
Up(r, Dfs0(i - 1, (s * 10 + v) % P, Z | !v & !z, e && (v == end), z && !v));
return r;
}
int Dfs(int i, int s, LL K, bool e, bool z) {
if(!i) return K == 1 ? s : 0;
int t = m[K]; if(!t) return 0;
if(!e && !z) return (1LL * s * f[i][t] % P * pw[i] + g[i][t]) % P;
int end = e ? bit[i] : 9, r = 0;
if(z) Up(r, Dfs(i - 1, s, K, e && !end, 1));
for(int v = 1;v <= end; ++v)
if(!(K % v)) Up(r, Dfs(i - 1, (s * 10 + v) % P, K / v, e && (v == end), 0));
return r;
}
int Cal(long long x) {
int tp = 0; for(;x; x /= 10) bit[++tp] = x % 10;
if(K != 0) return Dfs(tp, 0, K, 1, 1);
else return Dfs0(tp, 0, 0, 1, 1);
}
int main() {
Pre();
int T; for(scanf("%d", &T); T--; ) {
LL A, B; scanf("%lld%lld%lld", &A, &B, &K);
printf("%d\n", (Cal(B) - Cal(A - 1) + P) % P);
}
return 0;
}