一.题目
题目描述
给定一个长度为 n 的数组 A 1 , A 2 , ⋅ ⋅ ⋅ , A n A_1,A_2,⋅⋅⋅,A_n A1,A2,⋅⋅⋅,An。
你可以从中选出两个数 A i A_i Ai 和 A j A_j Aj(i 不等于 j),然后将 A i A_i Ai 和 A j A_j Aj 一前一后拼成一个新的整数。
例如 12 和 345 可以拼成 12345 或 34512。
注意交换 A i A_i Ai 和 A j A_j Aj 的顺序总是被视为 2 种拼法,即便是 A i = A j A_i = A_j Ai=Aj 时。
请你计算有多少种拼法满足拼出的整数是 K 的倍数。
输入格式
请你计算有多少种拼法满足拼出的整数是 K 的倍数。
第二行包含 n 个整数 A 1 , A 2 , ⋅ ⋅ ⋅ , A n A_1,A_2,⋅⋅⋅,A_n A1,A2,⋅⋅⋅,An。
输出格式
输出一个整数代表答案。
数据范围
1 ≤ n ≤ 105,
1 ≤ K ≤ 105,
1 ≤
A
i
A_i
Ai ≤ 109
输入样例1
4 2
1 2 3 4
输出样例1
6
二.解释
看完题目,想着先交一手暴力代码,提交完一分没有全部超时!!!
暴力不行,得优化一下,我们来找找规律。
设有
A
i
、
A
j
、
L
i
、
L
j
A_i、A_j、L_i、L_j
Ai、Aj、Li、Lj,其中
A
i
A_i
Ai 和
A
j
A_j
Aj 是序列中的两个数,
L
i
、
L
j
L_i、L_j
Li、Lj 则是这两个数的长度,此时这两个数可以拼接的结果有:
A
i
∗
1
0
L
j
+
A
j
A_i * 10^{L_j} + A_j
Ai∗10Lj+Aj
A
j
∗
1
0
L
i
+
A
i
A_j * 10^{L_i} + A_i
Aj∗10Li+Ai
两种。
所以我们的目标就是找到所有符合 ( A i ∗ 1 0 L j + A j A_i * 10^{L_j} + A_j Ai∗10Lj+Aj)% K = 0 和 ( A j ∗ 1 0 L i + A i A_j * 10^{L_i} + A_i Aj∗10Li+Ai)% K = 0 的数对个数。
当 ( A i ∗ 1 0 L j + A j A_i * 10^{L_j} + A_j Ai∗10Lj+Aj)% K = 0 时,我们可以都得到 ( A i ∗ 1 0 L j A_i * 10^{L_j} Ai∗10Lj) % K + A j A_j Aj % K 等于 0 或 K。
则:( K -( A j A_j Aj % K ))% K = ( A i ∗ 1 0 L j A_i * 10^{L_j} Ai∗10Lj) % K,等号左边最外面的 %K 是为了兼顾 等于 0 或 K 两种情况。
所以我们可以预处理一个哈希存放有乘 10 的 i 次方摸 K 的余数是 j 的数据个数,例如有 ( A i ∗ 1 0 L j A_i * 10^{L_j} Ai∗10Lj) % K 则 Map[ L j L_j Lj][( A i ∗ 1 0 L j A_i * 10^{L_j} Ai∗10Lj) % K] 加一。
三.代码
AC代码:
#include <iostream>
#include <unordered_map>
#include <algorithm>
#include <cstring>
#include <string>
#include <vector>
#include <queue>
#include <set>
using namespace std;
typedef unsigned long long int64;
const int MaxN = 1e5 + 10;
int64 InN, InK, Res;
int64 Ns[MaxN];
int64 LT[] = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000, 10000000000, 100000000000, 1000000000000 };
int64 Map[11][MaxN]; //Map[i][j]: 有多少个数乘10的i次方摸InK的余数是j
int GetLen(int64 InA)
{
int C = 0;
while (InA)
{
C++;
InA /= 10;
}
return C;
}
int main()
{
cin >> InN >> InK;
for (int64 i = 1; i <= InN; i++)
{
scanf("%llu", Ns + i);
for (int64 j = 1; j < 11; j++)
{
Map[j][(Ns[i] * LT[j]) % InK]++;
}
}
for (int64 i = 1; i <= InN; i++)
{
//遍历到的数作为尾数
int Len = GetLen(Ns[i]);
Res += Map[Len][(InK - (Ns[i] % InK)) % InK]; //多少个数乘10的尾数长度次方摸InK的余数加尾数摸InK等于0或InK
if ((Ns[i] * LT[Len]) % InK == (InK - (Ns[i] % InK)) % InK) { Res--; } //减去当尾数包含在结果中的情况
}
cout << Res;
return 0;
}