样例输入:
4 2
1 2 3 4
样例输出:
6
分析:对于一个数a和b,我们要求a和b连接后的数对k取余的结果,我们可以对这个问题进行简化
假设a=p1*k+r1,b=p2*k+r2,b的长度为l,那么ab就等于a*10^l+b,那么ab%k=(a*10^l+b)%k=((p1*k+r1)*10^l+p2*k+r2)%k=(r1*10^l+r2)%k
那么也就是说我们只需要记录a和b对k取余后的值以及b的位数就可以知道ab连接后对k取余的值,假如说我们现在知道第一个数是a,他对k取余后的值为r1,现在让我们求一个1<=b<=10^9,使得ab连接后的数对k取余后的值为0,那么我们可以考虑枚举b的位数,也就是枚举l,其中b对k的余数为x,那么我们就有
(r1*10^l+x)%k=0,由于r1和l都是已知的,那么我们就可以求出x,那么对于所有的满足位数为l,且余数为x的数b都是满足题意的。
由上面的分析我们可以发现,对于一个数a,我们需要知道另一个数对k取余后的结果以及位数来判断另一个数是否满足题意,那么我们可以枚举所有的a,对于每一个a,直接枚举b的位数,然后求出所有满足余数的b的个数,这个我们是可以直接在输入a数组时进行统计的,所以这样就可以直接得到答案。需要统计的数组定义如下:
f[i][j]记录长度为j且对k取余后结果为i的数的个数
需要注意的是如果a和他自己拼接之后满足是k的倍数,那么我们要对答案进行减1,防止多统计这种情况。
细节见代码:
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<map>
#include<queue>
#include<vector>
#include<cmath>
using namespace std;
const int N=1e5+10;
int f[N][11];//f[i][j]记录长度为j且对k取余后结果为i的数的个数
long long a[N],len[N];
long long ppow(long long a,long long b)
{
long long ans=1;
for(int i=1;i<=b;i++)
ans*=a;
return ans;
}
int main()
{
int n,k;
cin>>n>>k;
for(int i=1;i<=n;i++)
{
scanf("%lld",&a[i]);
long long t=a[i];
while(t)
{
t/=10;
len[i]++;
}
f[a[i]%k][len[i]]++;
}
long long ans=0;
for(int i=1;i<=n;i++)
{
long long r=a[i]%k;
if((r*ppow(10,len[i])+r)%k==0) ans--;//防止自身与自身匹配
for(int j=1;j<=10;j++)//枚举长度
{
long long tr=(r*ppow(10,j)%k);
tr=(k-tr)%k;
ans+=f[tr][j];
}
}
printf("%lld\n",ans);
return 0;
}