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;
}
posted @ 2020-06-12 10:05  Tian-Xing  阅读(134)  评论(0编辑  收藏  举报