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(Ai∗10len(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==5、65%35==30;
3535%35==03535\%35==03535%35==0,其中3500%35==0、35%35==03500\%35==0、35\%35==03500%35==0、35%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==(mod−a[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%mod为jjj的数有多少个。
此外,注意题意要求下标不能相同,但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;
}