版权声明:Powered By Fighter https://blog.csdn.net/qq_30115697/article/details/89320864
Solution
我们发现,题目中要求的"把a接到b前面"实际上等于 。于是我们自然想到预处理出 ,显然 不会超过10。然后对于每个 ,查询有多少数接到自己前面可以被 整除。
预处理和枚举 的 复杂度是不可避免的,那么我们就考虑如何加快询问速度。
最容易想到的是,我们对于每个
,把
插入到一个map
中。这样最多开10个map
,并且每次查询只需
的时间,理论上可以通过此题。
然而到这一步的时候,我就看见同机房巨佬的
被卡到了自闭,最后用了unordered_map
才勉强卡过(话说unordered_map
是不是需要c++11啊,哪位大佬科普一下)。然后才想起来
有巨大的常数,于是我们想进一步优化。
我们发现,这里的
实际上大材小用了,我们只需要简单的插入和查询,并不需要容器有序,于是我们可以自然的想到用unordered_map哈希表进行优化。虽然哈希表时间复杂度比较玄学,但再怎么样也不会比一只
再加大常数的map
慢。而且哈希表复杂度几乎可以看做常数级别。
所以我们就可以愉快地水掉一道紫题啦!!
Code
#include <bits/stdc++.h>
#define ll long long
#define MAX 200005
#define P 10007
using namespace std;
ll n, k;
ll a[MAX], p[20], lg[MAX];
struct hash_map {
int head[MAX], Next[MAX], cnt;
ll val[MAX], tot[MAX];
hash_map(){
cnt = 0;
memset(head, 0, sizeof(head));
memset(Next, 0, sizeof(Next));
memset(val, 0, sizeof(val));
memset(tot, 0, sizeof(tot));
}
void insert(ll x) {
ll k = x%P;
for(int i = head[k]; i; i = Next[i]) {
if(val[i] == x) {
tot[i]++;
return;
}
}
cnt++;
Next[cnt] = head[k];
head[k] = cnt;
val[cnt] = x;
tot[cnt] = 1;
}
ll find(ll x) {
ll k = x%P;
for(int i = head[k]; i; i = Next[i]) {
if(val[i] == x) {
return tot[i];
}
}
return 0;
}
}mp[15];
int main() {
cin >> n >> k;
p[0] = 1;
for(int i = 1; i <= 10; i++) {
p[i] = p[i-1]*10%k;
}
for(int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
ll tmp = a[i];
while(tmp) {
lg[i]++;
tmp /= 10;
}
}
for(int i = 1; i <= 10; i++){
for(int j = 1; j <= n; j++){
mp[i].insert(a[j]%k*p[i]%k);
}
}
ll t, ans = 0;
for(int i = 1; i <= n; i++){
t = ((k-a[i])%k+k)%k;
ans += mp[lg[i]].find(t);
if((a[i]%k*p[lg[i]]%k+a[i]%k)%k == 0){
ans--;
}
}
cout << ans << endl;
return 0;
}