题意:
给定n和k,表示一共n个数字,问将其中两个数字拼接起来能够被 k 整除的组合有多少种。比如:
6 11
45 1 10 12 11 7
答案就是7,一共是451、4510、110、1045、1012、121、1210可以整除11。
思路:
看到题目以后,先想的是两个数相加整除另一个数的条件是什么,然后一开始想是不是两个数都能整除一个数,这两个数再相加 才能整除这个数,然后当然显然不是。
接着再仔细想了想,拿了两个数实验了一下,发现这两个数 /k之后的小数部分相加刚好为1,于是便顺理成章地想到了两个数的%k的余数相加为k或者为0即能整除k。
因此再读入数的时候,就将不同位数的数%k的余数放到不同的数组中,一共10个数组,再记录每个数组的元素个数。
再对每个数进行遍历,比如45,就将45*10再*10,每次乘都求出该数%k的值,然后再在对应的区间内进行二分查找相应的余数,然后最后就是答案了。
注意:
可能一个数会和自己拼接,因此要注意将这部分的情况减去,还有就是可能会爆long long,我开的是unsigned long long。
最后感叹一下,虽然想出了正解,而且打出了代码,但是5、6个bug,最后还是没调出来...还要继续努力啊!!
代码:
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#define rep(i,a,b) for(ll i = a;i <= b;i++)
using namespace std;
const int N = 2*1e5+100;
typedef unsigned long long ll;
ll a[15][N];
int t[15];
ll num[N];
ll k,n;
int main()
{
while(~scanf("%I64u%I64u",&n,&k))
{
memset(t,0,sizeof t);
rep(i,1,n){
scanf("%I64u",&num[i]);
int cnt = 1;
ll h = 10;
while(h <= num[i]){h*=10; cnt++;}
a[cnt][++t[cnt]] = (num[i]%k);
}
rep(i,1,10)
{
sort(a[i]+1,a[i]+1+t[i]);
}
ll ans = 0;
rep(i,1,n)
{
ll x = 1;
int jud = 1;
rep(j,1,10)
{
x *= 10;
ll hm = num[i]*x;
ll tp = hm%k;
if(num[i] < x && jud == 1)
{
if((tp+num[i]%k) == k || (tp == 0 && (num[i]%k) == 0)){
ans--;
}
jud = 0;
}
ll arc = k-tp;
if(tp == 0) arc = 0;
int l = lower_bound(a[j]+1,a[j]+t[j]+1 , arc)- a[j] ;
int r = upper_bound(a[j]+1,a[j]+t[j]+1 , arc) - a[j] ;
r--;
int hmm = r-l+1;
hmm = max(0,hmm);
ans += hmm;
}
}
printf("%I64u\n",ans);
}
return 0;
}
/*
3 1000000000
1000000000 1000000000 1000000000
*/