【题目】 乘积末尾至少有X个0

题目:
从一个数组中取两个数相乘,问有多少种取法,使得取出的两个数的乘积末尾至少有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=22255
又因为 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=22255可以表示, 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

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值