题目
Problem Description
单身!
依然单身!
吉哥依然单身!
DS级码农吉哥依然单身!
所以,他生平最恨情人节,不管是214还是77,他都讨厌!
吉哥观察了214和77这两个数,发现:
2+1+4=7
7+7=72
77=711
最终,他发现原来这一切归根到底都是因为和7有关!所以,他现在甚至讨厌一切和7有关的数!
什么样的数和7有关呢?
如果一个整数符合下面3个条件之一,那么我们就说这个整数和7有关——
1、整数中某一位是7;
2、整数的每一位加起来的和是7的整数倍;
3、这个整数是7的整数倍;
现在问题来了:吉哥想知道在一定区间内和7无关的数字的平方和。
Input
输入数据的第一行是case数T(1 <= T <= 50),然后接下来的T行表示T个case;每个case在一行内包含两个正整数L, R(1 <= L <= R <= 10^18)。
Output
请计算[L,R]中和7无关的数字的平方和,并将结果对10^9 + 7 求模后输出。
Sample Input
3
1 9
10 11
17 17
Sample Output
236
221
0
题目分析
超级恶心的一道数位dp,不建议这方面刚入门的选手做,本身细节很多,要是对数位dp再不熟悉,那bug能de到哭。
题意很简单,求一个范围里与7无关的数的平方和。常规的数位dp好像都是求个数的,这里求平方和就是这题的第一个瓶颈(某种意义上,这是最简单的瓶颈了。。。。)
在这里,请确认这样一个基本公式:
∑
i
=
1
n
(
a
+
b
i
)
2
=
n
a
2
+
2
a
∑
i
=
1
n
b
i
+
∑
i
=
1
n
b
i
2
\sum_{i=1}^{n}(a+b_i)^2 = na^2+2a\sum_{i=1}^nb_i+\sum_{i=1}^{n}b_i^2
i=1∑n(a+bi)2=na2+2ai=1∑nbi+i=1∑nbi2这是这题求平方和的基本原理。这里a是进行dp时增加的高位数
(
i
∗
1
0
j
)
(i*10^j)
(i∗10j),这题的状态转移里这个式子很重要。它提供了从低位平方和到高位平方和的基本运算法则。
有了这个式子后,我们很自然地想到需要三个数组,平方和
(
∑
i
=
1
n
b
i
2
)
(\sum_{i=1}^{n}b_i^2)
(∑i=1nbi2),数字和
(
∑
i
=
1
n
b
i
)
(\sum_{i=1}^nb_i)
(∑i=1nbi),和符合的数个数
(
n
)
(n)
(n)。dp[i][j][k][m]表示第
i
i
i位,数字为
j
j
j,
%
7
\%7
%7为
k
k
k,各位加起来
%
7
\%7
%7为
m
m
m的数的平方和 / 和 / 个数。
状态转移方程也不难想
a
=
(
k
−
j
∗
1
0
i
%
7
+
7
)
%
7
,
b
=
(
m
−
j
+
14
)
%
7
a=(k-j*10^i\%7+7)\%7,b=(m-j+14)\%7
a=(k−j∗10i%7+7)%7,b=(m−j+14)%7
c
n
t
[
i
]
[
j
]
[
k
]
[
m
]
=
∑
p
=
1
9
c
n
t
[
i
−
1
]
[
p
]
[
a
]
[
b
]
cnt[i][j][k][m]=\sum_{p=1}^{9}cnt[i-1][p][a][b]
cnt[i][j][k][m]=p=1∑9cnt[i−1][p][a][b]
t
p
[
i
]
[
j
]
[
k
]
[
m
]
=
∑
i
=
1
9
(
c
n
t
[
i
−
1
]
[
p
]
[
a
]
[
b
]
∗
j
∗
1
0
i
+
t
p
[
i
−
1
]
[
p
]
[
a
]
[
b
]
)
tp[i][j][k][m]=\sum_{i=1}^{9}(cnt[i-1][p][a][b]*j*10^i+tp[i-1][p][a][b])
tp[i][j][k][m]=i=1∑9(cnt[i−1][p][a][b]∗j∗10i+tp[i−1][p][a][b])
d
p
[
i
]
[
j
]
[
k
]
[
m
]
=
∑
i
=
1
9
(
c
n
t
[
i
−
1
]
[
p
]
[
a
]
[
b
]
∗
(
j
∗
1
0
i
)
2
+
2
∗
j
∗
1
0
i
∗
t
p
[
i
−
1
]
[
a
]
[
b
]
+
d
p
[
i
−
1
]
[
p
]
[
a
]
[
b
]
)
dp[i][j][k][m]=\sum_{i=1}^{9}(cnt[i-1][p][a][b]*(j*10^i)^2+2*j*10^i*tp[i-1][a][b]+dp[i-1][p][a][b])
dp[i][j][k][m]=i=1∑9(cnt[i−1][p][a][b]∗(j∗10i)2+2∗j∗10i∗tp[i−1][a][b]+dp[i−1][p][a][b])题目中三个条件里,第一,不包含7,那么我们状态转移的时候碰到
j
=
7
j=7
j=7直接跳过就好,第二第三都是最后模
7
7
7不为
0
0
0,输出答案的时候保证
k
、
m
k、m
k、m不为
0
0
0即可。
坑点
1、最后的差值有可能是负的,所以需要先加上
m
o
d
mod
mod再取模。
2、
a
%
(
1
e
9
+
7
)
%
7
=
a
%
7
a\%(1e9+7)\%7 = a\%7
a%(1e9+7)%7=a%7是不一定成立的,所以预处理
1
0
i
10^i
10i数组的时候,需要另外打一个对
7
7
7取模的值的表,不能直接拿模过
(
1
e
9
+
7
)
(1e9+7)
(1e9+7)的值直接计算(当然,这是假定你打表的时候用
t
[
i
]
=
t
[
i
−
1
]
∗
10
%
m
o
d
t[i]=t[i-1]*10\%mod
t[i]=t[i−1]∗10%mod打,如果打表时没取模,到计算积的时候再取,这条就当屁处理)
3、时刻注意溢出问题
4、如果不做
1
e
18
1e18
1e18位的计算(确实也没意义),那么需要有特判
(
1
e
18
,
1
e
18
−
1
)
(1e18, 1e18-1)
(1e18,1e18−1)。
代码
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
#define ll long long
const ll mod = 1e9 + 7;
ll dp[20][10][7][7], tp[20][10][7][7], cnt[20][10][7][7]; //位数,首位数字,数%7,总和%7
//数组分别是平方和、sum、方案数
ll tb[20];
int num[20], mb[20];
void init()
{
for(int i = 0; i < 10; i++)
if(i != 7)
dp[0][i][i%7][i%7] = (ll)i*(ll)i, tp[0][i][i%7][i%7] = (ll)i, cnt[0][i][i%7][i%7] = 1;
for(int i = 1; i < 18; i++){
for(int j = 0; j < 10; j++){
if(j == 7) continue;
for(int k = 0; k < 7; k++){
for(int m = 0; m < 7; m++){
int c = (j * mb[i]) % 7, a = (k- c + 7) % 7, b = (m-j+14) % 7;
for(int p = 0; p < 10; p++){
if(p == 7) continue;
cnt[i][j][k][m] += cnt[i-1][p][a][b];
tp[i][j][k][m] += (cnt[i-1][p][a][b]*tb[i]%mod*(ll)j%mod + tp[i-1][p][a][b]) % mod;
dp[i][j][k][m] += ((ll)(j*j)*tb[i] % mod *tb[i]%mod*cnt[i-1][p][a][b] % mod + tb[i]*tp[i-1][p][a][b] % mod*(ll)(2*j) %mod + dp[i-1][p][a][b]) % mod;
cnt[i][j][k][m] %= mod, tp[i][j][k][m] %= mod, dp[i][j][k][m] %= mod;
}
}
}
}
}
}
ll query(ll a)
{
a++;
ll res = 0;
if(a > (ll)1e18) {a-- ;res = (a%mod)*(a%mod)%mod;}
if(a == (ll)1e18) a--;
ll c = 0;
memset(num, 0, sizeof(num));
int dex = 0, m1 = 0, m2 = 0;
while(a){
num[dex++] = (int)(a % 10);
a /= 10;
}
dex--;
while(dex >= 0){
int p = (7-m1)%7, q = (7-m2) % 7;
for(int i = 0; i < num[dex]; i++){
if(i == 7) continue;
for(int j = 0; j < 7; j++){
if(j == p) continue;
for(int k = 0; k < 7; k++){
if(k == q) continue;
res += (dp[dex][i][j][k] + 2*tp[dex][i][j][k]% mod *c %mod + cnt[dex][i][j][k]*c%mod*c%mod) % mod;
res %= mod;
}
}
}
m1 = (m1 + mb[dex]*num[dex]) % 7;
m2 = (m2 + num[dex]) % 7;
if(num[dex] == 7) break;
c += tb[dex] * (ll)num[dex] % mod, c %= mod ,dex--;
}
return res;
}
int main()
{
memset(dp, 0, sizeof(dp));
memset(tp, 0, sizeof(tp));
memset(cnt, 0, sizeof(cnt));
tb[0] = 1, mb[0] = 1;
for(int i = 1; i < 20; i++)
tb[i] = tb[i-1] * 10 % mod, mb[i] = (10*mb[i-1])%7;
init();
int t;
scanf("%d", &t);
for(int i = 1; i <= t; i++){
ll s, e;
scanf("%lld%lld", &s, &e);
printf("%lld\n", (query(e) - query(s-1) + mod) % mod);
}
}