[蓝桥杯 2020 省 B1] 整数拼接

一.题目

题目描述

给定一个长度为 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 一前一后拼成一个新的整数。

例如 12345 可以拼成 1234534512

注意交换 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 AiAjLiLj,其中 A i A_i Ai A j A_j Aj 是序列中的两个数, L i 、 L j L_i、L_j LiLj 则是这两个数的长度,此时这两个数可以拼接的结果有:
A i ∗ 1 0 L j + A j A_i * 10^{L_j} + A_j Ai10Lj+Aj
A j ∗ 1 0 L i + A i A_j * 10^{L_i} + A_i Aj10Li+Ai
两种。

所以我们的目标就是找到所有符合 A i ∗ 1 0 L j + A j A_i * 10^{L_j} + A_j Ai10Lj+Aj)% K = 0 A j ∗ 1 0 L i + A i A_j * 10^{L_i} + A_i Aj10Li+Ai)% K = 0 的数对个数。

A i ∗ 1 0 L j + A j A_i * 10^{L_j} + A_j Ai10Lj+Aj)% K = 0 时,我们可以都得到 ( A i ∗ 1 0 L j A_i * 10^{L_j} Ai10Lj) % K + A j A_j Aj % K 等于 0K

则:( K -( A j A_j Aj % K ))% K = ( A i ∗ 1 0 L j A_i * 10^{L_j} Ai10Lj) % K,等号左边最外面的 %K 是为了兼顾 等于 0K 两种情况。

所以我们可以预处理一个哈希存放有乘 10i 次方摸 K 的余数是 j 的数据个数,例如有 ( A i ∗ 1 0 L j A_i * 10^{L_j} Ai10Lj) % KMap[ L j L_j Lj][( A i ∗ 1 0 L j A_i * 10^{L_j} Ai10Lj) % 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;
}
  • 25
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值