关于状态转移,先简单的写这样一个式子:dp[i][j][k] = ∑dp[i-1][(j+dig)%7][(k*10+dig)%7](这里的求和符号不指加法,是一个抽象的意义)
其中dig是枚举的正在处理的数位i上所有可能的数字(这个式子只能帮助理解状态是如何转移的但是却不表示具体的运算,dp是结构体当然不能直接运算)
上面的等式,我们称等式左边表示总状态,等式右边为其子状态,显然总状态是等于所有子状态的“总和”(我说的状态的总和并非指加法运算)
那么怎么通过子状态算出总状态呢?
具体的计算:
先说几句废话:
对于13这个数,它的数位上的数是1,3,它的数位和是1+3,它自身的数值是 1*10+3
如果我知道数字13是与7无关的数,在其前面加一个1,也是与7无关的数,怎样计算在其前面加一个1之后的数的平方和的呢
(1*100)^2 + 2*(1*100)*13 + 13^2 这里相当于(100+13)^2
注意:在具体的状态转移中,我们是不知道13这个值,我们只知道有这么一个子状态
要求的是所有满足的数的平方和,所以最后具体的算式如下:
设总状态(当前状态)为ret,它其中一个子状态为nxt,枚举正在处理的这一数位上的数字为 i ,数位 i 在整个数字中具体的数值是i*10^pw10[pos]
那么有:
(1) ret.cnt += ret.cntnxt.cnt//这个与求个数的那类题型一样
(2) ret.sum += nxt.sum + i*10^pw10[pos]*nxt.cnt //之所以*数量,是因为后面有许多个符合条件的数,这一位可以跟后面所有符合条件的数搭配
(3) ret.sqsum += nxt.sqsum + 2*(i*10^pw10[pos])*nxt.sum + [(i*10^pw10[pos])^2]*nxt.cnt//这个是最难理解的式子,也就是是上面的完全平方式子,因为当前位固定,后面有很多满足情况的数位,所以与单独一个1配上后面13有点不一样,此处需要理解10分钟
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
typedef long long ll;
const ll mod = (ll)(1e9+7);
int digit[20], vis[20][10][10];
ll pw10[20];
struct Node
{
ll cnt, sum, sqsum;
Node(){cnt = -1; sum = 0; sqsum = 0;}
Node(ll _cnt, ll _sum, ll _sqsum): cnt(_cnt), sum(_sum), sqsum(_sqsum){}
}dp[20][10][10];
Node dfs(int pos, int sum_rem, int num_rem, bool limit)
{
if(pos == -1)
{
if(sum_rem == 0 || num_rem == 0)
return Node{0, 0 , 0};
else
return Node{1, 0, 0};
}
if(!limit && dp[pos][sum_rem][num_rem].cnt != -1)
return dp[pos][sum_rem][num_rem];
int last = limit ? digit[pos] : 9;
Node ret = Node{0, 0, 0};
for(int i = 0; i <= last; i++)
{
if(i == 7)
continue;
Node nxt = dfs(pos - 1, (sum_rem + i) % 7, (num_rem * 10 + i) % 7, limit && (i == last));
ret.cnt = (ret.cnt + nxt.cnt) % mod;
ret.sum = ((ret.sum + pw10[pos] * i % mod * nxt.cnt % mod) % mod + nxt.sum) % mod;
ret.sqsum = (ret.sqsum + nxt.sqsum) % mod;
ret.sqsum = (ret.sqsum + pw10[pos] * pw10[pos] % mod * i * i % mod * nxt.cnt % mod) % mod;
ret.sqsum = (ret.sqsum + 2 * pw10[pos] * i % mod * nxt.sum % mod) % mod;
}
if(!limit)
dp[pos][sum_rem][num_rem] = ret;
return ret;
}
ll solve(ll x)
{
memset(digit, 0, sizeof(digit));
int len = 0;
while(x)
{
digit[len++] = x % 10;
x /= 10;
}
return dfs(len-1, 0, 0, true).sqsum;
}
int main()
{
int T;
ll L, R;
pw10[0] = 1;
for(int i = 1; i <= 18; i++)
pw10[i] = pw10[i-1] * 10 % mod;
scanf("%d", &T);
while(T--)
{
scanf("%I64d%I64d", &L, &R);
ll ans = (solve(R) - solve(L-1)) % mod;
printf("%I64d\n", (ans + mod) % mod);
}
return 0;
}