提供一种序列分治的思路。还算比较general。
首先我们需要知道,0的个数取决于和
的个数的最小值。启示我们预处理出每一个数字中5的个数和2的个数。
根据的个数来进行排序,然后我们定义如下分治函数
,会将区间
分割为
.那么答案就拆分为计算左右区间之间跨区间的个数,然后分治计算
。
关键是如何跨区间,由于我们已经对的个数来排序了,那么对于右区间,我们可以从
往右遍历,然后对于左区间,从
开始从右往左遍历。
由于有序性质,我们可以枚举右区间,这个时候如果的2个数与
的2个数大于等于x,那么
可以减1,并且这是一个循环的过程,尝试找到更多的符合条件的元素,而且指针
不会回退。
这样我们只需要知道之间5的个数符合的元素。由于
中5的幂次很有限,30个左右,所以我们可以直接统计个数,然后暴力枚举计数。
更多序列分治题目,可见:https://www.cnblogs.com/MnZnOIerLzy/p/17812919.html
注意python得用pypy3.
import sys
# 输入加速
input = sys.stdin.readline
if __name__ == '__main__':
n, x = map(int, input().split())
arr = list(map(int, input().split()))
"""
预处理出,每一个数2和5的个数
5 * 5 * 2
2 * 2 * 2 * 2 * 5
选两个数,那么就是经典枚举右维护左
cur5 cur2 需要找同时满足 大于等于 x - cur5 x - cur2 的个数
可以排序
pre5 + 1 >= x - cur5 + 1
"""
lst = []
for i in range(n):
xx = arr[i]
cur5 = cur2 = 0
while xx % 5 == 0:
xx //= 5
cur5 += 1
while xx % 2 == 0:
xx //= 2
cur2 += 1
lst.append((cur2, cur5))
lst.sort()
# 第一维度双指针,第二维度暴力维护5的个数 题目所给例子的2和5个数情况:[(0, 0), (0, 1), (1, 0), (1, 2), (4, 1)]
def dfs(l, r) -> int:
if l == r:
return 0
mid = (l + r) // 2
cnt = 0
j = mid
mp5 = [0] * 31
for i in range(mid + 1, r + 1):
while j >= l and lst[j][0] + lst[i][0] >= x:
mp5[lst[j][1]] += 1
j -= 1
lb = max(0, x - lst[i][1])
for k in range(lb, 31):
cnt += mp5[k]
return cnt + dfs(l, mid) + dfs(mid + 1, r)
print(dfs(0, n - 1))