如何找到一个数组的所有子数组的所有GCD的总和?

时间:2019-08-29 00:49:36

标签: python arrays algorithm greatest-common-divisor

给出长度为n = 10 ^ 5的数组A。我必须有效地找到该数组所有子数组的GCD之和。

import math
def lgcd(a):
    g = a[0]
    for i in range(1,len(a)):
        g = math.gcd(g,a[i])
    return g

n = int(input())
A = list(map(int,input().split()))
ans = 0
for i in range(n):
    for j in range(i,n):
        ans+=lgcd(A[i:j+1])

print(ans)

2 个答案:

答案 0 :(得分:4)

首先要标记的是GCD(A [i-1:j])* d = GCD(A [i:j]),其中d是自然数。因此,对于固定子阵列端,将有一些块的GCD相等,并且不超过* log n *个块,因为一个块中的GCD是另一个块的GCD的除数,因此至少是小两倍。

因此可以使用下一个算法:对于数组中的所有元素,假定它们是子数组的最后一个元素,然后将找到所有GCD相等的块,并将它们加到我们的总和*块大小\ *块gcd *

要快速查找GCD相等的块,可以使用段树。要找到一个块,您只需要找到以固定元素结尾的最长子数组,其中gcd较小,然后在先前检查的块上。

此算法为O(* n \ * log n \ * log k *),其中k是数组中的最大值。

示例:

对于A = [120,15,36,20]

对于120,只有一个块-120(GCD(120)= 120)。所以总和现在是0 + 120 * 1 = 120

对于15,只有一个长度为2到15的块(GCD(15)= 15,GCD(120,15)= 15)。因此,总和现在为120 + 15 * 2 = 150。

对于36,有两个块-36(GCD(36)= 36)和3(GCD(15,36)= GCD(120,15,36)= 3)。因此,总和现在为192 = 150 + 36 * 1 + 3 * 2 = 192。

对于20有三个块-20(GCD(20)= 20),4(GCD(36,20)= 4)和1(GCD(15,36,20)= GCD(120,15,36) ,20)= 1)。所以总和现在是102 + 20 * 1 + 4 * 1 +1 * 2 = 218,这就是答案

答案 1 :(得分:1)

  

有效的方法是密切观察问题陈述。例子我们有数组为   [1,2,3,45]。现在,当我们找到[1,2]的GCD并需要找到[1,2,3]的GCD时,我们可以   只需使用GCD(GCD([1,2]),3)即可。因此,这里有重叠的子问题。因此,只需存储结果即可。

     

长度为k-1的子数组是由长度为k-1 +1个元素的子数组构成的。因此,包含第k个元素的子数组的GCD是第k个元素的GCD和第k-1个元素的子数组的GCD,即GCD(GCD(A [i:i + k],A [k])。

final = dict()

def gcd(a, b):
    if b == 0:
        return a
    return gcd(b, a % b)


A = [int(i) for i in input().split()]
dp = [[-1 for _ in range(len(A))] for _ in range(len(A))]

# Set GCD of single element as the elemnt itself
for i in range(len(A)):
    dp[i][i] = A[i]

# For every other subarrays of length l
# Calculate gcd using  length l - 1
for i in range(0, len(A)):
    for j in range(i + 1, len(A)):

        dp[i][j] = gcd(dp[i][j - 1], A[j])
        final[tuple(A[i: j + 1])] = dp[i][j]

print(final)