【题解】CF1029D Concatenated Multiples

原题传送门

Solution

我们发现,题目中要求的"把a接到b前面"实际上等于 a ∗ 1 0 l o g 10 b + b a*10^{log_{10}b}+b a10log10b+b。于是我们自然想到预处理出 a i ∗ 1 0 x m o d    k a_i*10^x \mod k ai10xmodk,显然 x x x不会超过10。然后对于每个 a i a_i ai,查询有多少数接到自己前面可以被 k k k整除。

预处理和枚举 a i a_i ai O ( n ) O(n) O(n)复杂度是不可避免的,那么我们就考虑如何加快询问速度。

最容易想到的是,我们对于每个 x x x,把 a i x m o d    k a_i^{x}\mod k aixmodk插入到一个map​中。这样最多开10个map,并且每次查询只需 l o g 2 n log_2n log2n的时间,理论上可以通过此题。

然而到这一步的时候,我就看见同机房巨佬的 m a p map map被卡到了自闭,最后用了unordered_map才勉强卡过(话说unordered_map是不是需要c++11啊,哪位大佬科普一下)。然后才想起来 m a p map map有巨大的常数,于是我们想进一步优化。

我们发现,这里的 m a p map map实际上大材小用了,我们只需要简单的插入和查询,并不需要容器有序,于是我们可以自然的想到用unordered_map哈希表进行优化。虽然哈希表时间复杂度比较玄学,但再怎么样也不会比一只 l o g log log再加大常数的map慢。而且哈希表复杂度几乎可以看做常数级别。

所以我们就可以愉快地水掉一道紫题啦!!

Code

#include <bits/stdc++.h>
#define ll long long
#define MAX 200005
#define P 10007
using namespace std;

ll n, k;
ll a[MAX], p[20], lg[MAX];
struct hash_map {
    int head[MAX], Next[MAX], cnt;
    ll val[MAX], tot[MAX];
    
    hash_map(){
        cnt = 0;
        memset(head, 0, sizeof(head));
        memset(Next, 0, sizeof(Next));
        memset(val, 0, sizeof(val));
        memset(tot, 0, sizeof(tot));
    }
    
    void insert(ll x) {
        ll k = x%P;
        for(int i = head[k]; i; i = Next[i]) {
            if(val[i] == x) {
                tot[i]++;
                return;
            }
        }
        cnt++;
        Next[cnt] = head[k];
        head[k] = cnt;
        val[cnt] = x;
        tot[cnt] = 1;
    }

    ll find(ll x) {
        ll k = x%P;
        for(int i = head[k]; i; i = Next[i]) {
            if(val[i] == x) {
                return tot[i];
            }
        }
        return 0;
    }
}mp[15];

int main() {
    cin >> n >> k;
    p[0] = 1;
    for(int i = 1; i <= 10; i++) {
        p[i] = p[i-1]*10%k;
    }
    for(int i = 1; i <= n; i++) {
        scanf("%d", &a[i]);
        ll tmp = a[i];
        while(tmp) {
            lg[i]++;
            tmp /= 10;
        }
    }
    for(int i = 1; i <= 10; i++){
        for(int j = 1; j <= n; j++){
            mp[i].insert(a[j]%k*p[i]%k);
        }
    }
    
    ll t, ans = 0;
    for(int i = 1; i <= n; i++){
        t = ((k-a[i])%k+k)%k;
        ans += mp[lg[i]].find(t);
        if((a[i]%k*p[lg[i]]%k+a[i]%k)%k == 0){
            ans--;
        }
    }
    cout << ans << endl;

    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值