AcWing:3734. 求和

题目

用 f(x) 来表示满足下列条件的最小正整数 a:

  1. a≥x。
  2. a 的各个数位不包含除了 4 和 7 以外的其他数字。

现在,给定两个整数 l,r(l≤r),请你计算 f(l)+f(l+1)+…+f® 的值。

输入格式

一行,两个整数 l,r。

输出格式

一行,一个整数表示求得的和。

  • 数据范围

前三个测试点满足 1 ≤ l ≤ r ≤ 10,
所有测试点满足 1 ≤ l ≤ r ≤ 1e9。

输入样例1:

2 7

输出样例1:

33

输入样例2:

7 7

输出样例2:

7

思路简介 构造 ,手段:DFS + 枚举

l , r最大长度可到 1e9 , 明显直接暴力枚举会TLE

那我们来分析一下 f(x)
当 f(x) 的位数只有零位时, f(x) 为                               ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~                              
当 f(x) 的位数只有一位时, f(x) 可为               ~~~~~~~~~~~~~               4,                         ~~~~~~~~~~~~~~~~~~~~~~~                         7
当 f(x) 的位数只有两位时, f(x) 可为       ~~~~~       44,          ~~~~~~~~          47,           ~~~~~~~~~           74,         ~~~~~~~         77
当 f(x) 的位数只有三位时, f(x) 可为 444, 447, 474, 477, 744, 747, 774, 777

相信大家不难看出, f(x)的位数为 u 时, f(x)的值就为 位数为 u - 1的f(x)的所有值 * 10 + 4(和 7)

即 f(x) 在位数为 1 时, f(x) 有 2^1 个数
即 f(x) 在位数为 2 时, f(x) 有 2^2 个数
即 f(x) 在位数为 len 时, f(x) 有 2^len 个数

而位数最长也就是 10 , 故 f(x)的总数 为 2^1 + 2^2 + … + 2^10 大约也就 2000 项
我们可以先枚举出来 f(x) 的所有情况(我采用dfs + vector), 对其排序, 判断 区间[l, r]上的数 在 f(x) 上对应的值

判断方法:

  1. 枚举 以 相邻的两个f(x)为端点所有区间
  2. 判断区间(l, r)与所枚举的区间 数字的重合个数
  3. 重合个数乘上右端点的值, 把结果存入(累加)答案中

样例1 [2, 7] ->       ~~~~~       2 3 4 5 6 7 (判断[l, r]在枚举区间有多少个重合数)
枚举区间 (0,4] ->   ~   4 4 4            ~~~~~~~~~~            和为 3 * 4
枚举区间 (4,7] ->            ~~~~~~~~~~            7 7 7   ~   和为 3 * 7

样例2 [1, 10] ->   ~   1 2 3 4 5 6 7 8 9 10 (判断[l, r]在枚举区间有多少个重叠数)
枚举区间 (0,4] -> 4 4 4 4                          ~~~~~~~~~~~~~~~~~~~~~~~~                          和为 4 * 4
枚举区间 (4,7] ->             ~~~~~~~~~~~             7 7 7                  ~~~~~~~~~~~~~~~~                  和为 3 * 7
枚举区间 (7,44] ->                   ~~~~~~~~~~~~~~~~~                   44 44 44    ~~    和为 3 * 44

最后输出答案(累加的和) 即可

具体操作见代码和注释

  • AC Code
#include <iostream>
#include <algorithm>
#include <vector>
typedef long long LL;
using namespace std;
vector<LL> all_47;

void dfs(int len,LL x) {  // 使用 dfs 来枚举出所有只包含 4 和 7 的数
    all_47.push_back(x);  // 存入容器
    if (len == 10) return ;  // 出口, 当 len 等于 10 时, 对应情况枚举结束 
    dfs(len + 1, x * 10 + 4);  // 该数后 + 4
    dfs(len + 1, x * 10 + 7);  // 该数后 + 7
}

int main() {
    dfs(0, 0);  // 枚举出所有只包含 4 和 7 的数
    sort(all_47.begin(), all_47.end());  // 排序

    LL l, r; cin >> l >> r;
    LL res = 0;  // 用来累加区间的和
    for (int i = 1; i < all_47.size(); i++) {
        LL a = all_47[i -1] + 1, b = all_47[i];  // a 为左区间(+1是为了保证左开右闭,防止加错了值), b 为右区间
        res += all_47[i] * max(0ll, min(r, b) - max(l, a) + 1);  // 区间重合数字个数 = 区间重合长度 + 1
            //  区间重合长度 = 两个右区间的最小值 减去 两个左区间的最大值, 当结果为负数时, 没有重合, 故还需要 它 与 0 取最大值
    }
    cout << res << "\n";
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

.Zero

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值