[打表] aw3734. 求和(打表+dfs+区间交集+aw周赛006_2)

1. 题目来源

链接:3734. 求和

2. 题目解析

打表大法好。

每个数只能由 4 或 7 构成,最大不超过 1e9,那么就是 10 位数,4 444 444 444 就是最大的了。每位有 2 种选法 4 / 7,那么总共就是 2 1 + 2 2 + . . . + 2 10 2^1+2^2+...+2^{10} 21+22+...+210 差不多 2000 多种选法。

所以可选方案很少,不管是打表还是 dfs 都很简单的可以搞出来。

[l, r] 区间中的各个数就能找到对应的 f f f 值,剩下的就转化成一个区间覆盖问题了。

y总板书:
在这里插入图片描述

最后将问题转化为区间求交集问题上很巧妙,且在求法上也非常巧妙。[l, r] 可能是个长区间,用一个个短区间 [q[i-1]+1, q[i]] 来慢慢将其覆盖,求区间交集,若有交集则本段 f f f 均为 q[i],若没交集,则区间长度为非正值,需要和 0 取 max 就判断出了,很是巧妙。


注意:

  • 注意开 LL
  • 区间覆盖动笔画一画…干想 WA 两次,动笔秒解决。

时间复杂度: O ( n ) O(n) O(n)

空间复杂度: O ( n ) O(n) O(n)


dfs预处理+区间求交集:

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

typedef long long LL;

LL l, r;
vector<LL> q;

// 很裸的 dfs,不太容易用二进制枚举写
// 有一个 0 在首位!
void dfs(int u, LL x) {
    q.push_back(x);
    if (u == 10) return ;
    dfs(u + 1, x * 10 + 4);
    dfs(u + 1, x * 10 + 7);
}

int main() {
    dfs(0, 0);
    sort(q.begin(), q.end());
    
    cin >> l >> r;
    LL res = 0;
    // 求 [l, r] 区间与 [q[i-1]+1, q[i]] 区间的交集,交集部分,f 值均为 q[i]
    // 没有交集时,应为 0
    // 注意学习区间求交集的方法
    for (int i = 1; i < q.size(); i ++ ) {
        LL a = q[i - 1] + 1, b = q[i];
        res += q[i] * max(0ll, (min(r, b) - max(l, a) + 1));
    }
    
    cout << res << endl;
    
    return 0;
}

直接打表,找规律,新数位在旧数位的基础上扩增两倍。

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

typedef long long LL;

LL l, r;
vector<LL> a[10];
vector<LL> q;

/*
[l, r]
4, 7, 
44, 47, 74, 77, 
444, 447, 474, 477, 744, 747, 774, 777 

[l, r] = [1, 9] = 4 * 4 + 7 * 3 + 44 * 2 = 125
*/

int main() {
    cin >> l >> r;
    a[0].push_back(4), a[0].push_back(7);
    for (int i = 1; i < 10; i ++ ) {
        for (int j = 0; j < a[i - 1].size(); j ++ ) { 
        	// 最高位在上一位基础上扩增两倍
            string s = to_string(a[i - 1][j]);
            a[i].push_back(stoll('4' + s));
            a[i].push_back(stoll('7' + s));
        }
    }
    
    for (auto e : a) 
        for (auto c : e) 
            q.push_back(c);

    sort(q.begin(), q.end());
    
    // 处理区间覆盖,动笔画!!!
    LL res = 0;
    int le = lower_bound(q.begin(), q.end(), l) - q.begin();
    int ri = lower_bound(q.begin(), q.end(), r) - q.begin();
    
    while (l <= r) {
        if (q[le] > r) {
            le -- ;
            res += (r - q[le]) * q[ ++ le];
            break;
        }
        else if (l <= q[le]) {
            res += (q[le] - l + 1) * q[le];
            l = q[le] + 1;
            le ++ ;
            
        }
    }
    
    cout << res << endl;
    
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Ypuyu

如果帮助到你,可以请作者喝水~

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

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

打赏作者

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

抵扣说明:

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

余额充值