首页 > 其他分享 >串联数字

串联数字

时间:2022-09-04 16:33:40浏览次数:100  
标签:串联 10 数字 int ++ leq 枚举 哈希

串联数字

给定 $n$ 个正整数 $a_1,a_2, \dots ,a_n$。

我们规定将正整数 $a_i$ 和 $a_j$ 串联是指将 $a_j$ 直接接在 $a_i$ 后面构成一个新整数。

例如,$12$ 和 $34$ 串联得到 $1234$,$34$ 和 $12$ 串联得到 $3412$。

现在,给定一个正整数 $k$,请你计算有多少个有序数对 $(i,j)(i \ne j)$ 满足,$a_i$ 和 $a_j$ 串联得到的整数能够被 $k$ 整除。

输入格式

第一行包含两个整数 $n,k$。

第二行包含 $n$ 个正整数 $a_1,a_2, \dots ,a_n$。

输出格式

一个整数,表示满足条件的有序数对的数量。

数据范围

前 $6$ 个测试点满足 $1 \leq n \leq 6$。
所有测试点满足 $1 \leq n \leq 2 \times {10}^{5}$,$2 \leq k \leq {10}^{9}$,$1 \leq a_i \leq {10}^{9}$。

输入样例1:

6 11
45 1 10 12 11 7

输出样例1:

7

 

输入样例2:

4 2
2 78 4 10

输出样例2:

12

 

输入样例3:

5 2
3 7 19 3 3

输出样例3:

0

 

解题思路

  这题之前有做过:整数拼接。不过$k$的取值范围扩大到了${10}^{9}$,而且也不能用unordered_map,否则会TLE,必须要手写哈希表。因此完全可以按照那题的思路来做,只不过哈希表要自己手写实现。

  讲一下y总思路。如果我们枚举到了$a_i$,现在看一下前面有多少个$a_j,~ (0 \leq j < i)$满足$k \mid \overline{a_{j}a_{i}}$,关键就在于如何快速判断前面有多少个$a_j$能够满足这个条件。

  $k \mid \overline{a_{j}a_{i}}$等价于$a_{j} \times {10}^{t} + a_{i} \equiv 0 ~~(mod~k)$,即$a_{j} \times {10}^{t} \equiv -a_{i} ~~(mod~k)$,其中$t$是$a_i$在十进制下的位数。当枚举到$a_i$时,$t$就是个定值,因此问题就变成了前面有多少个$a_j$乘${10}^{t}$后模$k$为$-a_{i}$。可以想到用哈希表来记录之前出现过的$a_j \times {10}^{t} ~mod~ k$。$t$的范围很小,为$1 \sim 10$,因此每次枚举完$a_i$后,存一下$a_i$乘${10}^{1}, {10}^{2}, \dots, {10}^{10}$模$k$的值,在哈希表中该值出现的次数加$1$。因此当枚举到$a_i$时,求一下$a_i$的位数$t$,然后直接查哈希表得到前面有多少个数乘${10}^{t}$模$k$为$-a_i$。哈希表有两个关键字,一个是余数$r$,一个是位数$t$,为了方便把这个两个数组合成一个long long$r \times 100 + t$来作为关键字。

  还有一点就是这题要枚举到$a_i$在$a_j$后面和前面两种情况,上面的做法只枚举到$a_i$在$a_j$后面,第一遍求完后还需要从后往前枚举来求$a_i$在$a_j$前面的情况,其实只需要把数组翻转然后再套第一遍的做法从左到右扫描就好了.

  AC代码如下:

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 typedef long long LL;
 5 
 6 const int N = 2e5 + 10, M = 1e7 + 3;
 7 
 8 int n, m;
 9 int a[N];
10 LL h[M];
11 int cnt[M];
12 
13 int find(int r, int t) {
14     LL x = r * 100ll + t;   // r是余数,t是位数
15     int k = x % M;
16     while (h[k] != -1 && h[k] != x) {
17         if (++k == M) k = 0;
18     }
19     
20     return k;
21 }
22 
23 LL solve() {
24     LL ret = 0;
25     memset(h, -1, sizeof(h));
26     memset(cnt, 0, sizeof(cnt));
27     for (int i = 0; i < n; i++) {
28         int r = (-a[i] % m + m) % m;
29         int t = 0, x = a[i];
30         while (x) {
31             t++;
32             x /= 10;
33         }
34         ret += cnt[find(r, t)];
35         
36         for (int j = 1, x = 10; j <= 10; j++, x = x * 10ll % m) {   // 统计(a[i]*10^k)%m的出现次数
37             int t = find(1ll * a[i] * x % m , j);
38             if (h[t] == -1) h[t] = 1ll * a[i] * x % m * 100ll + j;
39             cnt[t]++;
40         }
41     }
42     
43     return ret;
44 }
45 
46 int main() {
47     scanf("%d %d", &n, &m);
48     for (int i = 0; i < n; i++) {
49         scanf("%d", a + i);
50     }
51     
52     LL ret = solve();   // 先正着求一遍
53     reverse(a, a + n);  // 反转数组
54     ret += solve();     // 再正着求一遍
55     
56     printf("%lld", ret);
57     
58     return 0;
59 }

  我当时的思路是$a_i$在前面而$a_j$在后面,对于一个$a_i$,本质是枚举所有$a_j,~ (i \ne j)$,求有多少个$a_j$满足$a_{i} \times {10}^{t} \equiv -a_{j} ~~(mod~m)$,其中这里的$t$是$a_j$的数位。因此可以预处理用哈希表来统计每个$-a_i \% k$出现的次数。然后枚举$a_i$,对于每个$a_i$,$k$都从$1$开始枚举到$10$,查哈希表统计$a_i \times {10}^k \% m$出现的次数,也就是$a_j$(数位为$k$)的满足条件个数。

  AC代码如下:

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 typedef long long LL;
 5 
 6 const int N = 5e6 + 3;
 7 
 8 int a[N], cnt[11][N], len[N];
 9 int h[N];
10 
11 int find(int x) {
12     int k = x % N;
13     while (h[k] != -1 && h[k] != x) {
14         if (++k == N) k = 0;
15     }
16     return k;
17 }
18 
19 int main() {
20     int n, m;
21     scanf("%d %d", &n, &m);
22     memset(h, -1, sizeof(h));
23     for (int i = 0; i < n; i++) {
24         scanf("%d", a + i);
25         int t = a[i];
26         while (t) {
27             len[i]++;
28             t /= 10;
29         }
30         
31         t = (1ll * -a[i] % m + m) % m;
32         int x = find(t);
33         if (h[x] == -1) h[x] = t;
34         cnt[len[i]][x]++;
35     }
36     
37     LL ret = 0;
38     for (int i = 0; i < n; i++) {
39         LL t = a[i];
40         for (int j = 1; j <= 10; j++) {
41             t = t * 10 % m;
42             ret += cnt[j][find(t)];
43         }
44         
45         t = a[i];
46         for (int j = 0; j < len[i]; j++) {
47             t = t * 10 % m;
48         }
49         if (t == (1ll * -a[i] % m + m) % m) ret--;  // 如果有a[i]*10^t = -a[i] (mod m),这种情况下i==j,要特判掉
50     }
51     
52     printf("%lld", ret);
53     
54     return 0;
55 }

 

参考资料

  AcWing 4611. 串联数字(AcWing杯 - 周赛):https://www.acwing.com/video/4297/

标签:串联,10,数字,int,++,leq,枚举,哈希
From: https://www.cnblogs.com/onlyblues/p/16655309.html

相关文章

  • 数字图像处理概述
    计算机视觉技术任务:通过对采集的图片或视频进行处理以获得相应场景的相关信息。流程:视频处理以图像处理为基础图像数据最流行的表示方式:数字图像。成像方式不止可......
  • 448. 找到所有数组中消失的数字
     思路难度简单1065收藏分享切换为英文接收动态反馈给你一个含 n 个整数的数组 nums ,其中 nums[i] 在区间 [1,n] 内。请你找出所有在 [1,n] 范围内但......
  • 报告分享|2022年中国制造业数字化转型研究报告
    全文链接:http://tecdat.cn/?p=28398  《2022年中国制造业数字化转型研究报告》立足制造业整体,围绕制造业数字化转型的系列问题进行讨论,试图帮助企业建立对数字化转型的......
  • 报告分享|2022中国财税数字化转型研究报告
    阅读全文:http://tecdat.cn/?p=28389财税助率化利型是个黄杂金距,作为经济其限的重要“配套服务”和“基础发挥”,财税行业正经历典型的,然震撼大,力世界的结构性负化,晕涉多方......
  • 13. 罗马数字转整数
    罗马数字包含以下七种字符:I,V,X,L,C,D和M。 字符数值I1V5X10L50C100D500M1000例如,罗马数字2写做II,即为两个并列的1。12写做XII,即为X+II......
  • 645. 错误的集合 (丢失了一个数字 并且 有一个数字重复)
     labuladong题解思路难度简单286收藏分享切换为英文接收动态反馈集合 s 包含从 1 到 n 的整数。不幸的是,因为数据错误,导致集合里面某一个数字复制了成了集......
  • delphi 【数字微调编辑框组件TcxSpinEdit】
      此控件仅支持数字数.默认情况下不支持小数点,但支持负数输入.1.设置控件支持小数点输入.properties---valueType:=vtFloat2.隐藏边框右边的微调按钮.pr......
  • 从“数字工厂”到“物联工厂”5G+智慧工厂数据采集监控方案
    近年来,我国高度重视智能制造产业发展,政策举措密集出台,为资金、技术、支撑平台等提供源源不断的政策支持,推进新一代信息技术和制造业融合发展。以智慧工厂为例,作为......
  • 图扑数字孪生空冷机组,助推智慧电厂拥抱“双碳”
    前言空冷岛是电厂空气冷却装置的一个形象称谓,主要由56台风机组成,功能为高温蒸汽降温。空气冷却装置原理是利用自然界的空气来对工艺流体进行冷凝的大型工业用热交换......
  • 将string类型数字保留两位小数,并转换成千分位的格式
    保留两位小数,并转换成千分位的格式将一个string类型的数字,保留两位小数,并转换成千分位的格式//返回保留两位小数的字符串privateStringsaveTwo(Stringstr){......