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

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 ( n 2 ) O(n^2) O(n2)
思路②:我们观察到拼成的两个数 A i A j A_iA_j AiAj有以下性质:
( A i ∗ 1 0 l e n ( A j ) % m + A j % m ) % m = = 0 (A_i*10^{len(A_j)}\%m+A_j\%m)\%m==0 (Ai10len(Aj)%m+Aj%m)%m==0
举个例子: 3465 % 35 = = 0 3465\%35==0 3465%35==0,其中 3400 % 35 = = 5 、 65 % 35 = = 30 3400\%35==5、65\%35==30 3400%35==565%35==30
3535 % 35 = = 0 3535\%35==0 3535%35==0,其中 3500 % 35 = = 0 、 35 % 35 = = 0 3500\%35==0、35\%35==0 3500%35==035%35==0
因此我们发现,用两个数拼接出来的数只有对 m o d mod mod取模相加再对 m o d mod mod取模为0时,或者对 m o d mod mod取模都为0且相等时才能整除 m o d mod mod。而这两种情况,可归结为取模相加再取模为0。
因此,我们对每个数 a [ i ] a[i] a[i]找到它前面加上哪些数可以凑出 ( a [ i ] % m o d + x % m o d ) % m o d = = 0 (a[i]\%mod+x\%mod)\%mod==0 (a[i]%mod+x%mod)%mod==0,也就是要找到所有 x % m o d = = ( m o d − a [ i ] % m o d ) % m o d x\%mod==(mod-a[i]\%mod)\%mod x%mod==(moda[i]%mod)%mod的、且能放到 a [ i ] a[i] a[i]前面的(也就是恰好乘了 1 0 a [ i ] 10^{a[i]} 10a[i]的数)。
由此,我们预处理出所有 c [ i ] [ j ] : c[i][j]: c[i][j]:所有向左移 i i i位(也就是乘了 1 0 i 10^i 10i),且 % m o d \%mod %mod j j j的数有多少个。
此外,注意题意要求下标不能相同,但 a [ i ] ∗ 1 0 a [ i ] a[i]*10^{a[i]} a[i]10a[i]也有可能可以加到 a [ i ] a[i] a[i]前,因此判断 ( a [ i ] % m o d + 1 0 a [ i ] % m o d ) % m o d = = 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、付费专栏及课程。

余额充值