恨 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;
}

参考博客
https://www.cnblogs.com/littlehb/p/15800476.html

posted on   itdef  阅读(2)  评论(0编辑  收藏  举报

相关博文:
阅读排行:
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 周边上新:园子的第一款马克杯温暖上架
历史上的今天:
2021-03-04 剑指 Offer 57. 和为s的两个数字 哈希 双指针 二分查找
2016-03-04 asio 广播代码示例

导航

< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

统计

点击右上角即可分享
微信分享提示