Luogu P5842 【[SCOI2012]Blinker 的仰慕者】
Description
Solution
不难想到这题用数位\(dp\)解决。
那么首先可以想到\(dp_{i, j, num2, num3, num5, num7}\)表示从高到低填了\(i\)位,是否卡位,乘积中的每个质因子数量是多少此时的方案数,\(sum_{i, j, num2, num3, num5, num7}\)表示这些数的和。
这样子的转移是很好想的不再赘述,但是这样定义状态空间会爆炸,大概需要\(\log_2 {2 ^ {64}} \times \log_3 {2 ^ {64}} \times \log_5 {2 ^ {64}} \times \log_7 {2 ^ {64}} \times 2 \times 19 \times 2= 125296640\)的空间。
这时注意到实际上没必要用这么多状态,所有可行的状态只有大概\(66061\)个,那么我们可以把所有的可行状态都扔到一个哈希表里,就解决了空间的问题。
接下来就可以按照套路先预处理出\(f_{i, j}\)表示从低到高\(i\)位,乘积在哈希表里的位置是\(j\)的方案数,\(sum_{i, j}\)表示此时的和。
剩下的就是如何卡位以及算贡献。
从高位到低位枚举一下\(lcp\)然后枚举这位填的数字\(j\)。先判断填\(j\)是否可行,也就是说看看\(lcp\)和\(j\)的积是否整除\(k\)。只要这一位不是填的最高位那么后面可以随便填,所以这一位如果可行就在最终的答案里加上\((lcp \times 10 + j) * 10^{i - 1} * f_{i - 1, k / lcp / j} + g_{i - 1, k / lcp / j}\)。
最后别忘了加上\(\sum_{i = 1}^{n - 1}g_{i, k}\)。
对于卡位,每一位都卡到最高数字要求只算一遍贡献。我们每次枚举每一位填什么的时候填小于当前位的数字,这时我们发现所有位都卡位的情况没有算上,那么可以将要算的数先加一,这样保证只在最后一位的时候算上了贡献。
注意\(k\)的取值可能是\(0\),这是我们就要求至少有一位是\(0\)并且有数字出现过,这就是一个基础的数位\(dp\),此处不再赘述。
Code
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int MOD = 20120427, HashMOD = 1000037, zt = 66062;
#define ll long long
int head[HashMOD + 50], num, f[19][zt], g[19][zt], cnt, zhi[20], pow[19], dp[20][2][2][2], sum[20][2][2][2];
struct Node
{
int next;
ll zhi;
} hash[HashMOD + 50];
template <class T>
void Read(T &x)
{
x = 0; int p = 0; char st = getchar();
while (st < '0' || st > '9') p = (st == '-'), st = getchar();
while (st >= '0' && st <= '9') x = (x << 1) + (x << 3) + st - '0', st = getchar();
x = p ? -x : x;
return;
}
template <class T>
void Put(T x)
{
if (x < 0) putchar('-'), x = -x;
if (x > 9) Put(x / 10);
putchar(x % 10 + '0');
return;
}
int Ins(ll x)
{
int bh = x % HashMOD;
for (int i = head[bh]; i; i = hash[i].next)
if (hash[i].zhi == x) return i;
hash[++num] = (Node){head[bh], x};
head[bh] = num;
return num;
}
int Ask(ll x)
{
int bh = x % HashMOD;
for (int i = head[bh]; i; i = hash[i].next)
if (hash[i].zhi == x) return i;
return 0;
}
void Prework()
{
pow[0] = 1;
for (int i = 1; i <= 18; i++) pow[i] = (pow[i -1] * 10) % MOD;
int tmp;
f[0][Ins(1)] = 1;
for (int i = 0; i < 18; i++)
for (int j = 1; j <= num; j++)
for (int k = 1; k <= 9; k++)
{
if (hash[j].zhi * k > 1000000000000000000LL) break;
(f[i + 1][tmp = Ins(hash[j].zhi * k)] += f[i][j]) %= MOD,
(g[i + 1][tmp] += g[i][j] * 10 % MOD + f[i][j] * k) %= MOD;
}
return;
}
int Work(ll x, ll k)
{
cnt = 0;
while (x)
{
zhi[++cnt] = x % 10;
x /= 10;
}
ll ans = 0, pre = 0, mul = 1;
for (int i = cnt; i; i--)
{
if (!zhi[i]) break;
for (int j = 1; j < zhi[i]; j++)
if ((k % (mul * j)) == 0)
{
ll tmp = k / mul / j;
int t = Ask(tmp);
(ans += (pre * 10 + j) * pow[i - 1] % MOD * f[i - 1][t] % MOD + g[i - 1][t]) %= MOD;
}
pre = pre * 10 + zhi[i];
mul = mul * zhi[i];
}
int tmp = Ask(k);
for (int i = 1; i < cnt; i++) (ans += g[i][tmp]) %= MOD;
return ans;
}
int Work0(ll x)
{
cnt = 0;
memset(dp, 0, sizeof(dp));
memset(sum, 0, sizeof(sum));
while (x)
{
zhi[++cnt] = x % 10;
x /= 10;
}
dp[cnt + 1][1][0][0] = 1;
for (int i = cnt + 1; i >= 2; i--)
{
if (i != cnt + 1) dp[i][0][0][0] = 1;
for (int j = 0; j <= 1; j++)
for (int t = 0; t <= 1; t++)
for (int q = 0; q <= 1; q++)
if (dp[i][j][t][q])
for (int k = 0; k <= 9; k++)
{
if (j && k > zhi[i - 1]) break;
if (!t && !q && !k) continue;
(dp[i - 1][j && (k == zhi[i - 1])][t || (!k)][q || k] += dp[i][j][t][q]) %= MOD;
(sum[i - 1][j && (k == zhi[i - 1])][t || (!k)][q || k] += sum[i][j][t][q] * 10 + dp[i][j][t][q] * k) %= MOD;
}
}
return (sum[1][1][1][1] + sum[1][0][1][1]) % MOD;
}
int main()
{
Prework();
int t;
ll l, r, k;
Read(t);
while (t--)
{
Read(l); Read(r); Read(k);
if (k) Put((Work(r + 1, k) - Work(l, k) + MOD) % MOD), putchar('\n');
else Put((Work0(r) - Work0(l - 1) + MOD) % MOD), putchar('\n');
}
return 0;
}