【dtoj#5158】拼数

传送门

题目描述

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)(1i,jn,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 1n105,1k,ai109
对于前 30 % 30\% 30% 的数据,保证 n ≤ 1000 n\leq 1000 n1000
对于前 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;
}

Thanks!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值