串联数字
串联数字
给定 个正整数 。
我们规定将正整数 和 串联是指将 直接接在 后面构成一个新整数。
例如, 和 串联得到 , 和 串联得到 。
现在,给定一个正整数 ,请你计算有多少个有序数对 满足, 和 串联得到的整数能够被 整除。
输入格式
第一行包含两个整数 。
第二行包含 个正整数 。
输出格式
一个整数,表示满足条件的有序数对的数量。
数据范围
前 个测试点满足 。
所有测试点满足 ,,。
输入样例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
解题思路
这题之前有做过:整数拼接。不过的取值范围扩大到了,而且也不能用unordered_map,否则会TLE,必须要手写哈希表。因此完全可以按照那题的思路来做,只不过哈希表要自己手写实现。
讲一下y总思路。如果我们枚举到了,现在看一下前面有多少个满足,关键就在于如何快速判断前面有多少个能够满足这个条件。
等价于,即,其中是在十进制下的位数。当枚举到时,就是个定值,因此问题就变成了前面有多少个乘后模为。可以想到用哈希表来记录之前出现过的。的范围很小,为,因此每次枚举完后,存一下乘模的值,在哈希表中该值出现的次数加。因此当枚举到时,求一下的位数,然后直接查哈希表得到前面有多少个数乘模为。哈希表有两个关键字,一个是余数,一个是位数,为了方便把这个两个数组合成一个long long,即来作为关键字。
还有一点就是这题要枚举到在后面和前面两种情况,上面的做法只枚举到在后面,第一遍求完后还需要从后往前枚举来求在前面的情况,其实只需要把数组翻转然后再套第一遍的做法从左到右扫描就好了。
更新:补充对的理解,这里的是的位数。当枚举到时,此时是定值,也是定值。这时我们本来是要枚举所有的,看看有多少个满足,但我们发现一开始可以使用哈希表来预处理统计,使得判断满足条件的时间复杂度为。那么预处理统计什么东西呢?对于判断条件。此次就只有是一个变量,其他都是常量,即需要看有多少个在乘上模后的值恰好为。因此一开始需要预处理统计出数组的每个数乘上模后的值的出现次数。虽然说在枚举到时是个定值,但不同的的位数会不同,而的取值范围是,因此预处理时每个数都要乘上的若干个次方。然后在枚举到时,就是答案(当然还需要进行特判)。
同理,看成,一开始预处理统计每个数的的出现次数。
关键点就是预处理变量的部分,之前一直不是很理解。
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 }
我当时的思路是在前面而在后面,对于一个,本质是枚举所有,求有多少个满足,其中这里的是的数位。因此可以预处理用哈希表来统计每个出现的次数。然后枚举,对于每个,都从开始枚举到,查哈希表统计出现的次数,也就是(数位为)的满足条件个数。
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/
本文来自博客园,作者:onlyblues,转载请注明原文链接:https://www.cnblogs.com/onlyblues/p/16655309.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效