题目大意:
给出
个询问,
每个询问给出一组
,求一些在区间
的数,满足:
问这些数分别平方后的和对
取模的结果是多少。
一切在十进制下进行。
分析:
很毒瘤的一道题,细节调到心态爆炸
容斥一下可以发现,
对于一组询问
,
答案为可以被表示为
,然后这题就好做很多了
可以使用数位
,
假设我们要求
的贡献,
令
表示选到了第
位(从高位开始选),满足自身
,所有数位之和
, 数位中是否出现过
(出现为
,否则为
),是否满位(即与
的前
个高位是否相等,相等为
,否则为
),最后的
分别表示这样的数的个数,以及它们的和与它们的平方和。
设
表示数
的第
个高位的数值
那么初值:
枚举第一个高位的数值
,
,对应位置
,对应位置
,对应位置
运算:
时返回
,否则返回
然后我们思考一下如何转移:
假设我们已经计算了第
个高位的
,
那么我们考虑如何找到与第
个高位的关系,
枚举
,
,
,为
选到了第
位,满足数
,数位之和
,
的出现状态为
一类的数
①前
位中并没有满位,此时第
位可以随便选也不会出现满位的情况,
那么枚举第
位的选数情况,即
,设当前选
则可以转移:
+=
+=
+=
②前
位满位了,此时第
位选
时候不会满位,选
时会满位,
那么只需要将转移中的满位情况求出即可,转移同
最后的
的贡献就是
,len为
的位数
注意过程中取模以及取模会出现的负数情况即可。
代码:
#include <iostream>
#include <cstdio>
#include <cmath>
#include <queue>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
const int modn = 1000000007;
ll dp[20][10][10][2][2][3], num[20], L, R, A;
int T;
void read(ll &x)
{
ll f = 1; x = 0; char s = getchar();
while (s < '0' || s > '9') { if (s == '-') f = - 1; s = getchar(); }
while (s >= '0' && s <= '9') { x = x * 10 + (s - '0'); s = getchar(); }
x = x *f;
}
ll solve(ll x)
{
if (x == - 1 || x == 0) return 0;
ll cdp = 0; int len = 0;
for (; x; x /= 10) num[++len] = x % 10;
for (int i = 1; i <= len / 2; i++) swap(num[i], num[len - i + 1]);
memset(dp, 0, sizeof(dp));
for (int i = 0; i <= num[1]; i++)
{
dp[1][i % A][i % A][(i == A)][(i == num[1])][0] += 1;
dp[1][i % A][i % A][(i == A)][(i == num[1])][1] += i;
dp[1][i % A][i % A][(i == A)][(i == num[1])][2] += i * i;
}
// 位数 / 数位的和%A / 数的大小%A / 是否出现过A / 是否满位 / 个数,和,平方和
for (int i = 1; i < len; i++)
for (int j = 0; j < A; j++)
for (int k = 0; k < A; k++)
for (int l = 0; l <= 1; l++)
{
for (int wq = 0; wq <= 9; wq++)
{
dp[i + 1][(j + wq) % A][(k * 10 + wq) % A][l || (wq == A)][0][0] =
(dp[i + 1][(j + wq) % A][(k * 10 + wq) % A][l || (wq == A)][0][0] + dp[i][j][k][l][0][0]) % modn;
dp[i + 1][(j + wq) % A][(k * 10 + wq) % A][l || (wq == A)][0][1] =
(dp[i + 1][(j + wq) % A][(k * 10 + wq) % A][l || (wq == A)][0][1] + 10LL * dp[i][j][k][l][0][1] + 1LL * dp[i][j][k][l][0][0] * wq) % modn;
dp[i + 1][(j + wq) % A][(k * 10 + wq) % A][l || (wq == A)][0][2] =
(dp[i + 1][(j + wq) % A][(k * 10 + wq) % A][l || (wq == A)][0][2] + 100LL * dp[i][j][k][l][0][2] + 20LL * dp[i][j][k][l][0][1] * wq + 1LL * dp[i][j][k][l][0][0] * wq * wq) % modn;
}
for (int wq = 0; wq <= num[i + 1]; wq++)
{
dp[i + 1][(j + wq) % A][(k * 10 + wq) % A][l || (wq == A)][(wq == num[i + 1])][0] =
(dp[i + 1][(j + wq) % A][(k * 10 + wq) % A][l || (wq == A)][(wq == num[i + 1])][0] + dp[i][j][k][l][1][0]) % modn;
dp[i + 1][(j + wq) % A][(k * 10 + wq) % A][l || (wq == A)][(wq == num[i + 1])][1] =
(dp[i + 1][(j + wq) % A][(k * 10 + wq) % A][l || (wq == A)][(wq == num[i + 1])][1] + 10LL * dp[i][j][k][l][1][1] + 1LL * dp[i][j][k][l][1][0] * wq) % modn;
dp[i + 1][(j + wq) % A][(k * 10 + wq) % A][l || (wq == A)][(wq == num[i + 1])][2] =
(dp[i + 1][(j + wq) % A][(k * 10 + wq) % A][l || (wq == A)][(wq == num[i + 1])][2] + 100LL * dp[i][j][k][l][1][2] + 20LL * dp[i][j][k][l][1][1] * wq + 1LL * dp[i][j][k][l][1][0] * wq * wq) % modn;
}
}
// 位数 / 数位的和%A / 数的大小%A / 是否出现过A / 是否满位 / 个数,和,平方和
for (int i = 1; i < A; i++)
for (int j = 1; j < A; j++)
for (int k = 0; k <= 1; k++) cdp = (cdp + dp[len][i][j][0][k][2]) % modn;
return cdp;
}
int main()
{
scanf("%d", &T);
while(T--)
{
read(L); read(R); read(A);
printf("%lld\n" , ((solve(R) - solve(L - 1)) % modn + modn) % modn);
}
return 0;
}