题目描述
EndSaH 有
n
n
n 个数
a
1
,
a
2
,
.
.
.
,
a
n
a_1,a_2,...,a_n
a1,a2,...,an,他打算选出这些数中的两个数进行拼接。
一次拼数操作指的是将
x
,
y
x,y
x,y 两个正整数视作数字串,
x
x
x 在前
y
y
y在后拼接成一个新数字串,该新数字串所表示的正整数即这次拼数操作的结果。
例如将
1234
1234
1234 与
56
56
56 拼数,将得到结果
123456
123456
123456。
注意拼数操作是有顺序的,如拼接
56
56
56 与
1234
1234
1234 会得到
561234
561234
561234。
但不知道为什么,EndSaH 一点也不喜欢
k
k
k 的倍数。
他很好奇一个问题:有多少数对
(
i
,
j
)
(
1
≤
i
,
j
≤
n
,
i
≠
j
)
(i,j)(1\leq i,j\leq n,i\neq j)
(i,j)(1≤i,j≤n,i=j)满足对
a
i
a_i
ai 与
a
j
a_j
aj进行拼数操作后,结果是不是
k
k
k 的倍数?
由于他太菜了实在不会,只能向你求助了。
输入格式
第一行为两个正整数 n , k n,k n,k。
第二行为 n n n 个正整数,第 i i i 个数为 a i a_i ai。
输出格式
输出一行一个正整数,表示满足要求的数对的个数。
样例输入
4 11
45 1 10 12
样例输出
5
样例解释
数对 ( 2 , 1 ) , ( 3 , 2 ) , ( 2 , 4 ) , ( 1 , 4 ) , ( 4 , 1 ) (2,1),(3,2),(2,4),(1,4),(4,1) (2,1),(3,2),(2,4),(1,4),(4,1) 满足条件。
拼数所得结果分别为 145 , 101 , 112 , 4512 , 1245 145,101,112,4512,1245 145,101,112,4512,1245,均不是 11 11 11 的倍数。
数据范围与提示
对于所有数据,保证
1
≤
n
≤
1
0
5
,
1
≤
k
,
a
i
≤
1
0
9
1\leq n \leq 10^5,1\leq k,a_i\leq 10^9
1≤n≤105,1≤k,ai≤109。
对于前
30
%
30\%
30% 的数据,保证
n
≤
1000
n\leq 1000
n≤1000。
对于前
60
%
60\%
60% 的数据,保证
a
i
<
1
0
3
a_i<10^3
ai<103。
题解
考场上想出了正解,可是把 1 0 9 10^9 109给搞成了9位数,于是成功80分……
- 首先,根据n的数量算出全部数对的数量。
- 我们求得每个数乘以10的1~10次方(相当于十进制下左移1~10位)除以k的余数,并且统计出每一个余数在每一个乘方下被取到的次数,建一个二维map来存储它。
- 之后对于每一个数,求出它除以k的余数。
如果接在它前面的那个数左移后除以k的余数与它相加等于k或等于0(当时差点没查出错来)那就不满足条件,可以删除对应的数对数量。 - 最后,不用担心相同的数。如果相同的数组成的数对不符合条件,那么它一样会被多次统计次数;如果符合条件,因为题目求的是数对而不是数字,重复的数字是可以的。
代码:
#include<iostream>
#include<cstdio>
#include<map>
using namespace std;
map <long long,map<int,int> > bucket;
int k,num[100005],wss,tmp,tmpp;
long long ans,n;
int main(){
scanf("%lld%d",&n,&k);
for(int i=1;i<=n;i++){
scanf("%d",&num[i]);
tmp=num[i]%k;
tmp*=10;
for(int j=1;j<=10;j++){
tmp%=k;
bucket[tmp][j]++;
tmp*=10;
}
}
ans=n*(n-1);
for(int i=1;i<=n;i++){
wss=0;
tmp=tmpp=num[i];
tmpp%=k;
while(tmp>0){
tmp/=10;
tmpp*=10;
tmpp%=k;
wss++;
}
bucket[tmpp][wss]--;
ans-=bucket[k-(num[i]%k)][wss];
if(num[i]%k==0) ans-=bucket[0][wss];
bucket[tmpp][wss]++;
}
printf("%lld\n",ans);
return 0;
}