VJ的MNNUrank的E
题意:
给定数组A[] , 问两两数合并,有多少个合并数能被K整除。
思路:
情况① : a与b都可以整除k,他们的两个合并数必定都可以整除k
打个比方 a = 36 , b = 12 , k = 6
由于 (a % k == 0 && b % k == 0) , 可得 (a + b) % k == 0 且 (b + a) % k == 0 , 也就是3612 % 6 = 0 , 1236 % 6 = 0
情况② :排除情况①的其余情况,根据余数进行判定
首先,假设 a 和 b 进行合并 , a 有 n 位 ,b 有 m 位 :
假如合并方法是 a + b :那么实际上拼凑成的数是 a * 10 ^ m + b ,那么判断这个数是否整除k,也就是判断(a * 10 ^ m + b) % k == 0 ,
也就是判断(a * 10 ^ m + b)% k + b % k == 0 ?
由于情况①以及排除了 (a % k == 0 && b % k == 0) 的情况 , 所以只能当 (a * 10 ^ m + b)% k + b % k = k 时,情况才成立。
由于ai数据最大的是1e9 , n , m <= 9 , 所以我们可以直接计算出 (a * 10 ^ i)% k (i = 0 , 1 , 2 , 3 ....,9)的情况并记录下来.
然后对于位数为m , 模k后的值为temp的数 ,他作为尾巴(后置位)的对答案的贡献是:
个数 (位数为m,乘10 ^ 0 , 模k后的值为temp) * 个数(位数为1 ~ 9 ,乘10 ^ m , 模k后的值为 k - temp)
由此计算出所有的答案
代码:
#include<bits/stdc++.h> using namespace std; const int maxn = 1e5 + 10; #define ios std::ios::sync_with_stdio(false) int a[maxn][10];///(b[i] * 10^j) % k int b[maxn];///存数组 int num[maxn];///每个数有几位 int ma[10][10][maxn];///位数为i ,乘的数为10^j的,余数为k的,个数 signed main() { ios;cin.tie(0); int n , k; cin >> n >> k; for(int i = 1 ; i <= n ; i ++){ cin >> b[i]; a[i][0] = b[i] % k; for(int j = 1 ; j <= 9 ; j ++){ ///计算*1 *10 *100 ... 后的模数 a[i][j] = a[i][j - 1] * 10; a[i][j] %= k; } } for(int i = 1 ; i <= n ; i ++){ ///计算位数 int cnt = 0; int now = b[i]; while(now){ cnt ++ , now /= 10; } num[i] = cnt; } for(int i = 1 ; i <= n ; i ++){ for(int j = 0 ; j <= 9 ; j ++){ ma[num[i]][j][a[i][j]] ++;///每个数的位数,乘10 ^ j , 模值为a[i][j] } } int ans = 0; for(int i = 1 ; i < 10 ; i ++){ for(int j = 0 ; j < 10 ; j ++){ for(int l = 1 ; l < k ; l ++) ans += ma[i][0][l] * ma[j][i][k - l];///情况2 ,(位数为i,模数为l的作为尾巴)(位数为j,乘10 ^ i,模数为k - l作为头) } if(ma[i][0][0] == 0)continue; int sum = 0; for(int l = 0 ; l < 10 ; l ++) sum += ma[l][i][0]; ///情况1 ans += ma[i][0][0] * (sum - 1);///要排除掉自己和自己组合的那种 } cout << ans << '\n'; return 0; }