恨 7 不成妻 繁琐的数位DP
// 1590:恨 7 不成妻.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
/*
http://ybt.ssoier.cn:8088/problem_show.php?pid=1590
https://www.acwing.com/problem/content/description/1088/
https://loj.ac/p/10168
单身!
依然单身!
吉哥依然单身!
DS 级码农吉哥依然单身!
所以,他平生最恨情人节,不管是 214还是 77,他都讨厌!
吉哥观察了 214 和 77 这两个数,发现:
2+1+4=7
7+7=7×2
77=7×11
最终,他发现原来这一切归根到底都是因为和 77 有关!所以,他现在甚至讨厌一切和 7 有关的数!
什么样的数和 7 有关呢?如果一个整数符合下面三个条件之一,那么我们就说这个整数和 7 有关:
整数中某一位是 7;
整数的每一位加起来的和是 7 的整数倍;
这个整数是 7 的整数倍。
现在问题来了:吉哥想知道在一定区间内和 7 无关的数字的平方和。
【输入】
输入数据的第一行是测试数据组数 T,然后接下来的 T 行表示 T 组测试数据。
每组数据在一行内包含两个正整数 L,R。
【输出】
对于每组数据,请计算 [L,R] 中和 7无关的数字的平方和,并将结果对 109+7取模后输出。
【输入样例】
3
1 9
10 11
17 17
【输出样例】
236
221
0
数据范围
1≤T≤50,1≤L≤R≤10^18
*/
#include <iostream>
#include <cstring>
using namespace std;
typedef long long LL;
const int N = 20;
const LL mod = 1e9 + 7;
LL a[N]; //记录拆分后的每位数
LL base[N]; //预处理10的幂次方在mod后的结果
struct Node {
LL s0; //满足条件的个数
LL s1; //满足条件的数字和
LL s2; //满足条件的数字平方和
}f[N][7][7];
/*
维度1: u 数位位置索引
维度2: sum 前面数位上的值和 mod7
维度3:pre 前面数字和 mod7
op:是否超过输入数字改为的值 未超过 后面的数值可以取0~9 ,等于则下一位[u-1]的取值范围是0~~a[u-1]
*/
Node dfs(int u, int sum, int pre, bool op) {
//u==0 根据sum 不是7的倍数 且 pre 不是7的倍数,则数合法, 返回个数1,否则返回0
if (u == 0) {
if (sum && pre) return { 1,0,0 };
return { 0,0,0 };
}
//记忆化
if (!op && f[u][sum][pre].s0 != -1) return f[u][sum][pre];
//根据op判断可以取值的上界
int up = op ? a[u] : 9;
struct Node ans = { 0,0,0 };
for (int i = 0; i <= up; i++) {
if (i ==7) continue;
struct Node ret = dfs(u - 1, (sum + i) % 7, (pre*10+i)%7,op&&(i==up));
ans.s0 = (ans.s0 + ret.s0) % mod;
//ans.s1 = (ans.s1 + ret.s0 * i % mod * base[u - 1] % mod + ret.s1) % mod;
ans.s1 = (ans.s1 + i * base[u - 1] % mod * ret.s0 % mod + ret.s1) % mod;
// 汇集数字平方和
ans.s2 = (ans.s2 + i * base[u - 1] % mod * i % mod * base[u - 1] % mod * ret.s0 % mod) % mod;
//ans.s2 = (ans.s2 + 2 * i % mod * base[u - 1] % mod * ret.s1 % mod + ret.s2) % mod;
ans.s2 = (ans.s2 + 2 * i * base[u - 1] % mod * ret.s1 % mod + ret.s2) % mod;
}
if (!op) f[u][sum][pre] = ans;
return ans;
}
LL calc(LL x) {
int idx = 0;
while (x != 0) {
a[++idx] = x % 10;
x /= 10;
}
return dfs(idx, 0, 0, true).s2;
}
int main()
{
memset(f, -1, sizeof f);
// 预处理出10的幂次方【因为涉及到取模,要不这玩意还用预处理?】
base[0] = 1;
for (int i = 1; i < N; i++) base[i] = base[i - 1] * 10 % mod;
int T; cin >> T;
while (T--) {
LL l, r; cin >> l >> r;
cout << (calc(r) - calc(l-1) + mod) % mod << endl;
}
return 0;
}
作 者: itdef
欢迎转帖 请保持文本完整并注明出处
技术博客 http://www.cnblogs.com/itdef/
B站算法视频题解
https://space.bilibili.com/18508846
qq 151435887
gitee https://gitee.com/def/
欢迎c c++ 算法爱好者 windows驱动爱好者 服务器程序员沟通交流
如果觉得不错,欢迎点赞,你的鼓励就是我的动力
欢迎转帖 请保持文本完整并注明出处
技术博客 http://www.cnblogs.com/itdef/
B站算法视频题解
https://space.bilibili.com/18508846
qq 151435887
gitee https://gitee.com/def/
欢迎c c++ 算法爱好者 windows驱动爱好者 服务器程序员沟通交流
如果觉得不错,欢迎点赞,你的鼓励就是我的动力


【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 周边上新:园子的第一款马克杯温暖上架
2021-03-04 剑指 Offer 57. 和为s的两个数字 哈希 双指针 二分查找
2016-03-04 asio 广播代码示例