【蓝桥杯】整数小拼接(哈希+二分)

给定一个长度为 n 的数组 A1,A2,⋅⋅⋅,An。

你可以从中选出两个数 Ai 和 Aj(i 不等于 j),然后将 Ai 和 Aj 一前一后拼成一个新的整数。

例如 12 和 345 可以拼成 12345 或 34512。

注意交换 Ai 和 Aj 的顺序总是被视为 2 种拼法,即便是 Ai=Aj 时。

请你计算有多少种拼法满足拼出的整数小于等于 K。

输入格式
第一行包含 2 个整数 n 和 K。

第二行包含 n 个整数 A1,A2,⋅⋅⋅,An。

输出格式
一个整数代表答案。

数据范围
1≤n≤10^5,
1≤K≤10^10,
1≤Ai≤10^9
输入样例:
4 33
1 2 3 4
输出样例:
8

思路分析: 首先看数据量,就可以排除O(n2)做法了。十万的数据量意味着我们每个数只能枚举一次,也就说我们需要在把O(n2)的里面一个循环给优化成O(logn)或者O(n)。

回到题目,两个数目拼接可以看成是 A * 10x + B <= K,这里的x是与B的位数有关,观察等式B与和x成正相关,那么我们只需要用11个哈希表将 A * 10x 先存起来,然后枚举B即可。但是观察数据量,显然我们没办法直接把值映射到哈希表上面,因为数据太大了会爆空间。

接下来就到第二个难点了,通过转化等式变成 A *10x <= K - B。则可以发现,如果储存A的序列单调,并且等式也满足二段性。那么我们在哈希表预处理时将序列按单调存储,就可以在枚举B时二分查找A * 10x的值了。最后注意下题目数会非常大,所以记得开unsign long long。在查找完最后记得判重,如果l >= i 则包含自己,需要减一。至此题目拆分完毕,时间复杂度为O(nlogn)。

不了解二分的同学可以看这里

#include<bits/stdc++.h>
using namespace std;
const int N = 100010;
typedef long long ll;
typedef unsigned long long ull;
ull hash_table[11][N];
ll a[N],k;
int n;
ll cal(ull q[],int idx,ll target)
{
    if(q[0] > target) return 0;
    int l = 0,r = n - 1;
    while(l < r)
    {
        int mid = l + r + 1 >> 1;
        if(q[mid] <= target)
            l = mid;
        else
            r = mid - 1;
    }
    return l >= idx ? l : l + 1;
}
int main()
{
    cin >> n >> k;
    for (int i = 0; i < n; i ++ ) cin >> a[i];
    sort(a,a + n);
    
    for (int i = 0; i < n; i ++ )
    {
        ll t = a[i];
        for (int j = 0; j < 11; j ++ )
        {
            hash_table[j][i] = t;
            t = t * 10;
        }
    }
    
    ll res = 0;

    for (int i = 0; i < n; i ++ )
    {
        int len = to_string(a[i]).size();
        res += cal(hash_table[len],i,k - a[i]);
    }
    
    cout<<res<<endl;
    return 0;
}

在这里插入图片描述

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

老帅比阿

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

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

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

打赏作者

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

抵扣说明:

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

余额充值