吉哥系列故事——恨7不成妻
题意
一个正整数和 7 7 7 有关当且仅当满足以下条件之一:
- 数位中某一位是 7 7 7
- 数位和能被 7 7 7 整除
- 这个整数能被 7 7 7 整除
统计 [ l , r ] [l,r] [l,r] 内所有和 7 7 7 无关 的数字的 平方和
思路
这道题需要一点思维。我们先来看一个例子:
如果我们现在处理枚举
p
o
s
pos
pos 位为
p
p
p,低
p
o
s
−
1
pos - 1
pos−1 位的结果已经处理完了,在当前限制下,低
p
o
s
−
1
pos - 1
pos−1 的与
7
7
7 无关的数有:
x
1
,
x
2
,
x
3
x_1,x_2,x_3
x1,x2,x3 三个的话,那么它们的平方和应该是:
x
1
2
+
x
2
2
+
x
3
2
x_1 ^ 2 + x_2 ^ 2 + x_3 ^ 2
x12+x22+x32,现在我们要加上
p
p
p 的贡献,就是将
p
p
p 拼接上去,
p
p
p 这一位的值是
p
⋅
1
0
p
o
s
−
1
p \cdot 10^{pos - 1}
p⋅10pos−1,与
x
1
x_1
x1 拼接成:
p
x
1
px_1
px1,有:
(
p
x
1
)
2
=
(
p
⋅
1
0
p
o
s
−
1
+
x
1
)
2
=
(
p
⋅
1
0
p
o
s
−
1
)
2
+
x
1
2
+
2
⋅
x
1
⋅
(
p
⋅
1
0
p
o
s
−
1
)
(px_1)^2 = (p \cdot 10^{pos-1} + x_1)^2 = (p \cdot 10^{pos-1})^2 + x_1 ^ 2 + 2 \cdot x1 \cdot (p \cdot 10^{pos - 1})
(px1)2=(p⋅10pos−1+x1)2=(p⋅10pos−1)2+x12+2⋅x1⋅(p⋅10pos−1)
其实这里就是一个完全平方公式。
那么我们转移就可以通过维护更低位的平方和 s 2 s_2 s2,符合条件的数有多少个 c n t cnt cnt,符合条件的数的和 s 1 s_1 s1,三种信息。
转移就可以写成:
s
2
′
=
s
2
+
2
⋅
s
1
⋅
p
⋅
1
0
p
o
s
−
1
+
c
n
t
⋅
(
p
⋅
1
0
p
o
s
−
1
)
2
s_2\prime = s_2 + 2 \cdot s_1 \cdot p \cdot 10 ^{pos - 1} + cnt \cdot (p \cdot 10 ^ {pos -1})^2
s2′=s2+2⋅s1⋅p⋅10pos−1+cnt⋅(p⋅10pos−1)2
s
1
′
=
s
1
+
c
n
t
⋅
p
⋅
1
0
p
o
s
−
1
s_1\prime = s_1 + cnt \cdot p \cdot 10 ^ {pos - 1}
s1′=s1+cnt⋅p⋅10pos−1
c
n
t
′
=
c
n
t
cnt\prime = cnt
cnt′=cnt
我们可以用 d p [ p o s ] [ r 1 ] [ r 2 ] dp[pos][r_1][r_2] dp[pos][r1][r2] 来表示 p o s pos pos 个全变化位, r 1 r_1 r1 为当前数位和模 7 7 7 的余数, r 2 r_2 r2 为当前数模 7 7 7 的余数条件下的信息。
搜到最底层时所有的位已经确定,所以节点只有一个信息
c
n
t
=
1
cnt = 1
cnt=1 返回。
c
n
t
cnt
cnt 初始化为
−
1
-1
−1 是为了记忆化,这里的
Z
Z
Z 类型当成是一个会自动取模的
l
o
n
g
l
o
n
g
long long
longlong 就可以
时间复杂度: O ( l e n × 7 × 7 ) O(len \times 7 \times 7) O(len×7×7)
#include<bits/stdc++.h>
#define fore(i,l,r) for(int i=(int)(l);i<(int)(r);++i)
#define fi first
#define se second
#define endl '\n'
#define ull unsigned long long
#define ALL(v) v.begin(), v.end()
#define Debug(x, ed) std::cerr << #x << " = " << x << ed;
const int INF=0x3f3f3f3e;
const long long INFLL=1e18;
typedef long long ll;
template<class T>
constexpr T power(T a, ll b){
T res = 1;
while(b){
if(b&1) res = res * a;
a = a * a;
b >>= 1;
}
return res;
}
constexpr ll mul(ll a,ll b,ll mod){ //快速乘,避免两个long long相乘取模溢出
ll res = a * b - ll(1.L * a * b / mod) * mod;
res %= mod;
if(res < 0) res += mod; //误差
return res;
}
template<ll P>
struct MLL{
ll x;
constexpr MLL() = default;
constexpr MLL(ll x) : x(norm(x % getMod())) {}
static ll Mod;
constexpr static ll getMod(){
if(P > 0) return P;
return Mod;
}
constexpr static void setMod(int _Mod){
Mod = _Mod;
}
constexpr ll norm(ll x) const{
if(x < 0){
x += getMod();
}
if(x >= getMod()){
x -= getMod();
}
return x;
}
constexpr ll val() const{
return x;
}
explicit constexpr operator ll() const{
return x; //将结构体显示转换为ll类型: ll res = static_cast<ll>(OBJ)
}
constexpr MLL operator -() const{ //负号,等价于加上Mod
MLL res;
res.x = norm(getMod() - x);
return res;
}
constexpr MLL inv() const{
assert(x != 0);
return power(*this, getMod() - 2); //用费马小定理求逆
}
constexpr MLL& operator *= (MLL rhs) & { //& 表示“this”指针不能指向一个临时对象或const对象
x = mul(x, rhs.x, getMod()); //该函数只能被一个左值调用
return *this;
}
constexpr MLL& operator += (MLL rhs) & {
x = norm(x + rhs.x);
return *this;
}
constexpr MLL& operator -= (MLL rhs) & {
x = norm(x - rhs.x);
return *this;
}
constexpr MLL& operator /= (MLL rhs) & {
return *this *= rhs.inv();
}
friend constexpr MLL operator * (MLL lhs, MLL rhs){
MLL res = lhs;
res *= rhs;
return res;
}
friend constexpr MLL operator + (MLL lhs, MLL rhs){
MLL res = lhs;
res += rhs;
return res;
}
friend constexpr MLL operator - (MLL lhs, MLL rhs){
MLL res = lhs;
res -= rhs;
return res;
}
friend constexpr MLL operator / (MLL lhs, MLL rhs){
MLL res = lhs;
res /= rhs;
return res;
}
friend constexpr std::istream& operator >> (std::istream& is, MLL& a){
ll v;
is >> v;
a = MLL(v);
return is;
}
friend constexpr std::ostream& operator << (std::ostream& os, MLL& a){
return os << a.val();
}
friend constexpr bool operator == (MLL lhs, MLL rhs){
return lhs.val() == rhs.val();
}
friend constexpr bool operator != (MLL lhs, MLL rhs){
return lhs.val() != rhs.val();
}
};
const ll mod = 1e9 + 7;
using Z = MLL<mod>;
struct node{
Z cnt; //限制条件下与7无关的数字数量
Z s1; //与7无关的数字的和
Z s2; //与7无关的数字平方和
node(ll cnt = -1, ll s1 = 0, ll s2 = 0): cnt(cnt), s1(s1), s2(s2) {}
//cnt 初始化为 -1 是等价于memset的操作
};
node dp[20][8][8];
int num[20];
Z ten[20];
node dfs(int pos, int r1, int r2, bool limit){ //r1:当前数位和模7 r2:当前数模7
if(!pos) return (r1 && r2 ? node(1) : node(0));
if(!limit && dp[pos][r1][r2].cnt != -1) return dp[pos][r1][r2];
node res(0);
int up = (limit ? num[pos] : 9);
fore(i, 0, up + 1){
if(i == 7) continue;
node nxt = dfs(pos - 1, (r1 + i) % 7, (r2 * 10 + i) % 7, limit && i == up);
res.cnt += nxt.cnt;
res.s1 += nxt.cnt * i * ten[pos - 1] + nxt.s1;
res.s2 += nxt.s2 + nxt.cnt * i * i * ten[pos - 1] * ten[pos - 1];
res.s2 += 2 * nxt.s1 * i * ten[pos - 1];
}
if(!limit) dp[pos][r1][r2] = res;
return res;
}
Z solve(ll x){
int len = 0;
while(x){
num[++len] = x % 10;
x /= 10;
}
return dfs(len, 0, 0, true).s2;
}
int main(){
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout.tie(nullptr);
ten[0] = 1;
fore(i, 1, 19) ten[i] = ten[i - 1] * 10;
int t;
std::cin >> t;
while(t--){
ll l, r;
std::cin >> l >> r;
Z ans = solve(r) - solve(l - 1);
std::cout << ans << endl;
}
return 0;
}