题目:
从一个数组中取两个数相乘,问有多少种取法,使得取出的两个数的乘积末尾至少有X个0
思路:
末尾一个0,表示有一个2和5的因子,因为2*5=10;
所以一个数末尾有多少个0,及求它的因子中有多少个2和5,当然,是取2和5的数量的最小值,比如
200
=
2
∗
2
∗
2
∗
5
∗
5
200=2 * 2 *2 * 5 * 5
200=2∗2∗2∗5∗5
又因为
n
=
1
e
5
n=1e5
n=1e5,不能写两个for循环暴力遍历,那样的时间复杂度O(n^2),1e10>1e8,太大了
如下:
for (int i = 1; i <= n; i++) {
for (int j = i + 1; j <= n; j++) {
if (check(a[i] * a[j]))ans++;
}
}
考虑到,
2
30
2^{30}
230大于
1
e
9
1e9
1e9,
5
20
5^{20}
520大于
1
e
9
1e9
1e9,所以,可以先需处理出每个数有多少个因子
2
2
2 和因子
5
5
5,用数组
n
u
m
[
i
]
[
j
]
num[i][j]
num[i][j]表示,其中
i
i
i表示因子
2
2
2的个数,
j
j
j表示
5
5
5的个数(
i
i
i最大是
30
30
30,
j
j
j最大是
20
20
20)
比如
200
=
2
∗
2
∗
2
∗
5
∗
5
200=2 * 2 *2 * 5 * 5
200=2∗2∗2∗5∗5可以表示,
n
u
m
[
3
]
[
2
]
+
+
num[3][2]++
num[3][2]++,
for (int i = 1; i <= n; i++) {
int x = 0, y = 0;
while (a[i] % 2 == 0) {
x++;
a[i] /= 2;
}
while (a[i] % 5 == 0) {
y++;
a[i] /= 5;
}
num[x][y]++;
}
处理后,枚举 n u m [ i ] [ j ] num[i][j] num[i][j]数组就可以了,
for (int i = 0; i <= 30; i++) {
for (int j = 0; j <= 20; j++) {
if (num[i][j]) {
if (min(i + i, j + j) >= k) {
ans += num[i][j] * (num[i][j] - 1);//num[i][j]内部的数量,比如10=2*5,30=2*5*3,都属于num[1][1]
}
for (int x = 0; x <= 30; x++) {//外部的,比如10=2*5,100=2*2*5*5,属于num[1][1]和num[2][2]不一样
for (int y = 0; y <= 20; y++) {
if (a == i && b == j)continue;//已经算过了(内部)
if (min(i + x, j + b) >= k)ans += num[i][j] * num[x][y];//末尾0大于等于k
}
}
}
}
}
这样遍历把(a,b),(b,a)这种算成了两个,其实是一个,所以ans/2