题目传送门
题意:
给定n个数,将任意两个数连接起来。求有多少对这样的组合使得组合后的数能被k整除?
思路:
暴力肯定不行,暴力就是一个一个去组合,所以会超时。那我们换一种思路,给定一个数,寻找能与他匹配的数有多少个。这样只要n的复杂度,所以我们要预处理一下(我们就是巴一中漫无目的的寻找转化为有目的的寻找,暴力就是将两者绑定在一起然后才能判断,而预处理则是两者并没有必然联系,不会不可分离)。那么我们怎么将两者分开呢?比如1234 = 1200 + 34,我们分开算1200的余数,和34的余数,12后面有几个0,就是添加在他后面的数字的数位。那我们干脆直接求出每个数分别向左移动1-10位的余数。另外,要排除自己和自己组合的情况。
一个例子(帮助理解):
给出n个数和一个mod,求多少对加起来mod等于0?
这个n^2也不行,但是我们想一下如果我取余一个数=x 的话 我要什么情况才能让这个数加一个数%mod等于0,我们只有(x+y)%mod == 0 那在我们知道x的情况,我们只要找 mod-y的余数个数有多少即可。
还有一点提醒就是,这个可能会TLE,而且每次TLE的测试数据不固定,运气好就可以过,我就是AC了然后交AC代码有可能会TLE。
其他高手的链接
AC code:
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn = 2e5 + 10;
map<LL,LL> mp[11];//定义多维map
int n = 0,k = 0;
LL a[maxn];
int main(){
while(scanf("%d %d",&n,&k) != EOF){
//mp.clear();
for(int i = 0;i < 11;++i){//注意要清楚每个一维的map
mp[i].clear();
}
for(int i = 1;i <= n;++i){
scanf("%I64d",&a[i]);
LL x = a[i];
for(int j = 1;j <= 10;++j){//预处理,每个数分别向左移动j位mod k 的余数,
//方便后面直接查找
x *= 10;
x %= k;
++mp[j][x];
}
}
LL sum = 0;
for(int i = 1;i <= n;++i){
LL t = a[i] % k;
int len = log10(a[i]) + 1;//求出该数的数位(也就是他加在其他数后面时,其他数移动的位数)
sum += mp[len][(k - t) % k];//查找移动len位,余数与t互补
LL x = 1;
for(int j = 1;j <= len;++j){//排除是本身的情况
x = (x * 10) % k;
}
if(((a[i] * x)%k + a[i]%k)%k == 0){
--sum;
}
}
printf("%I64d\n",sum);
}
return 0;
}