第十一届蓝桥杯省赛CB第一场-9.整数拼接【同余】

该博客探讨了一种高效算法,用于解决给定数组中选择两个数拼接成整数,并判断该整数是否为特定模数的倍数的问题。通过观察拼接数的模运算性质,提出预处理并计算每数左侧可拼接的数,以减少时间复杂度。代码实现包括关键函数`q10`和遍历数组计算有效拼接方案的过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Date:2022.04.06
题意描述:
给定一个长度为 n 的数组 A1,A2,⋅⋅⋅,An。
你可以从中选出两个数 Ai 和 Aj(i 不等于 j),然后将 Ai 和 Aj 一前一后拼成一个新的整数。
例如 12 和 345 可以拼成 12345 或 34512。
注意交换 Ai 和 Aj 的顺序总是被视为 2 种拼法,即便是 Ai=Aj 时。
请你计算有多少种拼法满足拼出的整数是 K 的倍数。
输入格式
第一行包含 2 个整数 n 和 K。
第二行包含 n 个整数 A1,A2,⋅⋅⋅,An。
输出格式
一个整数代表答案。
数据范围
1≤n≤10^5,
1≤K≤10^5,
1≤Ai≤10^9
输入样例:
4 2
1 2 3 4
输出样例:
6
思路①:硬枚举O(n2)O(n^2)O(n2)
思路②:我们观察到拼成的两个数AiAjA_iA_jAiAj有以下性质:
(Ai∗10len(Aj)%m+Aj%m)%m==0(A_i*10^{len(A_j)}\%m+A_j\%m)\%m==0(Ai10len(Aj)%m+Aj%m)%m==0
举个例子:3465%35==03465\%35==03465%35==0,其中3400%35==5、65%35==303400\%35==5、65\%35==303400%35==565%35==30
3535%35==03535\%35==03535%35==0,其中3500%35==0、35%35==03500\%35==0、35\%35==03500%35==035%35==0
因此我们发现,用两个数拼接出来的数只有对modmodmod取模相加再对modmodmod取模为0时,或者对modmodmod取模都为0且相等时才能整除modmodmod。而这两种情况,可归结为取模相加再取模为0。
因此,我们对每个数a[i]a[i]a[i]找到它前面加上哪些数可以凑出(a[i]%mod+x%mod)%mod==0(a[i]\%mod+x\%mod)\%mod==0(a[i]%mod+x%mod)%mod==0,也就是要找到所有x%mod==(mod−a[i]%mod)%modx\%mod==(mod-a[i]\%mod)\%modx%mod==(moda[i]%mod)%mod的、且能放到a[i]a[i]a[i]前面的(也就是恰好乘了10a[i]10^{a[i]}10a[i]的数)。
由此,我们预处理出所有c[i][j]:c[i][j]:c[i][j]:所有向左移iii位(也就是乘了10i10^i10i),且%mod\%mod%modjjj的数有多少个。
此外,注意题意要求下标不能相同,但a[i]∗10a[i]a[i]*10^{a[i]}a[i]10a[i]也有可能可以加到a[i]a[i]a[i]前,因此判断(a[i]%mod+10a[i]%mod)%mod==0(a[i]\%mod+10^{a[i]}\%mod)\%mod==0(a[i]%mod+10a[i]%mod)%mod==0是否成立,若成立则说明他可以加到a[i]a[i]a[i]前,因此要把它去掉,-1。

代码如下:

#include <bits/stdc++.h>
using namespace std;
typedef unsigned long long LL;
const int N=1e5+10;
LL n,m,a[N],c[64][N],len[N];
LL q10(LL a,LL k,LL mod)
{
    LL x=a;
    for(int i=1;i<=k;i++) x=x*10%mod;
    return x;
}
int main()
{
	cin>>n>>m;memset(c,0,sizeof c);
	for(int i=1;i<=n;i++) 
	{
		cin>>a[i];string ss=to_string(a[i]);len[i]=ss.length();
		LL x=a[i],cnt=0;
		for(int j=1;j<=18;j++)
		{
			x=x*10%m;
			c[++cnt][x%m]++;
		}
	}
	LL ans=0;
	for(int i=1;i<=n;i++)
	{
		if(c[len[i]][(m-a[i]%m)%m])
			ans+=(c[len[i]][(m-a[i]%m)%m]);
		if((q10(a[i],len[i],m)%m+a[i]%m)%m==0) ans--;
	}
	cout<<ans;
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值