思路
依旧是数位DP, 我们需要过程中需要维护数字num%7和每一位的和sum%7。
所以定义DP[pos][mod1][mod2]表示选到pos位置num%7=mod1和sum%7=mod2的答案。
如果答案是要数字个数就好办了,但是答案要的是平方和。如果第二位我们去存当前选的数字num,而不是num%7,我们内存会炸,不可行。
大家应该还记得完全平方公式吧,即,假设要求一个两位数x的答案,假设第一位为pre,第二位为t,那么,所有符合要求的 x 的平方和怎么求呢?首先第一位选可行数字 i 的情况下,第二位选一个可行的值 t ,答案就需要加上此时选的数平方即。如果对于 i 的情况有多个可行的 t
,那么答案就是。推广到选多位数字的时候一样。
DP数组需要维护三个值,cnt 表示当前状态下后面随便选的可行数字个数,sum表示后面位的 ,sqsum表示 。
假设选到pos位置,当前选了 i 元素,深搜得到的dp值为 t 。
cnt 的维护比较简单,一路加过来就好了,
sum的维护,
sqsum(平方和)的维护,
然后注意下mod和long long即可。
因为取模的原因,最后R -(L-1)可能为负数所以要+mod在取余。
#include<bits/stdc++.h>
using namespace std;
const int MOD = 1e9+7;
typedef long long LL;
struct DP
{
LL cnt, sum, sqsum;
DP () {}
DP (LL cnt, LL sum, LL sqsum): cnt(cnt), sum(sum), sqsum(sqsum) {}
}dp[20][10][10];
LL ten[20];
string s;
DP dfs(int pos, int mod1, int mod2, bool limit)
{
if (pos == -1) return DP(mod1!=0 && mod2!=0, 0, 0);
if (!limit && dp[pos][mod1][mod2].cnt != -1) return dp[pos][mod1][mod2];
int E = limit?s[pos]-'0':9;
DP ans = DP(0, 0, 0);
for (int i = 0; i <= E; i++)
{
if (i == 7) continue;
DP t = dfs(pos-1, (mod1+i)%7, (mod2*10+i)%7, limit&&(i==E));
ans.cnt += t.cnt;
ans.cnt %= MOD;
ans.sum += t.sum + t.cnt*i%MOD*ten[pos]%MOD;
ans.sum %= MOD;
ans.sqsum += t.cnt*i*i%MOD*ten[pos]%MOD*ten[pos]%MOD;
ans.sqsum += t.sqsum + 2*i*ten[pos]%MOD*t.sum%MOD;
ans.sqsum %= MOD;
}
if (!limit) dp[pos][mod1][mod2] = ans;
return ans;
}
LL solve(LL x)
{
s = to_string(x); reverse(s.begin(), s.end());
return dfs(s.size()-1, 0, 0, 1).sqsum;
}
int main()
{
ten[0] = 1;
for (int i = 1; i < 20; i++) ten[i] = (ten[i-1]*10)%MOD;
memset(dp, -1, sizeof(dp));
int T; scanf("%d", &T);
while (T--)
{
LL L, R; scanf("%lld%lld", &L, &R);
printf("%lld\n", (solve(R)-solve(L-1)+MOD)%MOD);
}
return 0;
}
/*
3
1 9
10 11
17 17
*/