找出数组中l和r之间的乘积的对数,并将其包含在内。

问题描述 投票:1回答:1

这才是真正的问题

然而,它简化为

查找所有 SEMPIPRIMES (2个不同质因数的乘积,如6(2*3),范围从L到R)

将会有多个L和R的查询

由于N很大,我们不能预先计算半数。

但是,我们可以存储原始值,因为它们只到了 根据问题,10^6

现在,假设我有所有的质数,由 筛网

我需要所有可能的L到R之间的乘积对。

或问题简化为给定一个排序阵列。找出所有可能的搭配,包括L和R之间的产品。

我把这部分代码写进了编辑部。

for(int i=0; i<cnt and ar[i]<=r; i++)
{
    int lower = L/ar[i];
    if(L%ar[i]>0)
        lower++;
    lower = max(lower, ar[i]+1);
    int upper = R/ar[i];
    if(upper<lower)
        continue;
    ans += upper_bound(ar.begin(),ar.end(),upper)-
            lower_bound(ar.begin(),ar.end(),lower);
}
algorithm data-structures binary-search array-algorithms
1个回答
2
投票

这里有一个方法,这个方法可能不快,但似乎很合理。

  1. 10^8以下的质数约为5*10^6。

参考一下。https:/en.wikipedia.orgwikiPime -counting_function.

  1. 但是,我们可能不必保留所有的质数,因为这样做的效率相当低。我们可以只保留半初数。

半质数的生成过程已经有了。每个半质数都是 2 个不同质因数的乘积。

所以,我们可以保留一个数组,这个数组将存储所有的半质数,因为在这个范围内最多只有10^5个半质数,我们可以对这个数组进行排序。对于每次查询,我们只需要在数组上进行二进制搜索,就可以找到这个范围内的元素数。

  1. 那么,如何保存这些半元数呢?

我们可以稍微修改一下Eratosthenes的筛子来生成半数。

我们的想法是,我们将保留一个 countDivision 数组,它将存储范围内每个整数的除数。我们只考虑一个整数的半质数是 countDivision 该整数的索引值为2(2个除数)。

def createSemiPrimeSieve(n): 
    v = [0 for i in range(n + 1)] 

    # This array will initially store the indexes 
    # After performing below operations if any 
    # element of array becomes 1 this means 
    # that the given index is a semi-prime number 

    # Storing indices in each element of vector 
    for i in range(1, n + 1): 
        v[i] = i 

    countDivision = [0 for i in range(n + 1)] 

    for i in range(n + 1): 
        countDivision[i] = 2

    # This array will initially be initialized by 2 and 
    # will just count the divisions of a number 
    # As a semiprime number has only 2 prime factors 
    # which means after dividing by the 2 prime numbers 
    # if the index countDivision[x] = 0 and v[x] = 1 
    # this means that x is a semiprime number 

    # If number a is prime then its 
    # countDivision[a] = 2 and v[a] = a 

    for i in range(2, n + 1, 1): 

        # If v[i] != i this means that it is 
        # not a prime number as it contains 
        # a divisor which has already divided it 
        # same reason if countDivision[i] != 2 
        if (v[i] == i and countDivision[i] == 2): 

            # j goes for each factor of i 
            for j in range(2 * i, n + 1, i): 
                if (countDivision[j] > 0): 

                    # Dividing the number by i 
                    # and storing the dividend 
                    v[j] = int(v[j] / i) 

                    # Decreasing the countDivision 
                    countDivision[j] -= 1

    # A new vector to store all Semi Primes 
    res = [] 

    for i in range(2, n + 1, 1): 

        # If a number becomes one and 
        # its countDivision becomes 0 
        # it means the number has 
        # two prime divisors 
        if (v[i] == 1 and countDivision[i] == 0): 
            res.append(i) 

    return res 

信用。https:/www.geeksforgeeks.orgprint-all-semi-prime-numbers-less-than-or-equal-to-n

但是,生成的复杂度与筛子相同,即 O(nloglogn). 如果(R-L)是<10^5,这个方法就可以通过。但由于(R-L)可以大到10^8,所以不可行。

另一种方法是 计数 而不是生成。

让我们来研究一个例子。

2 10

现在,让我们假设,我们知道所有的质数,直到10^6(为 pq 不能超过10^6)。)

primes = [2, 3, 5, 7, 11, ...]

10^6以下的质数小于10^5(所以我们可以把它们存储在一个数组中),时间复杂度也是可以控制的。

现在,我们可以扫描我们的 primes 数组来计算每个质数的贡献,以生成范围(L,R)内的半质数。

首先,我们从2开始,借助2来生成多少个半质数。

让我们看看primes = [2, 3, 5, 7, 11, ..]

我们不能选择2,因为2和2不一样(p、q一定不一样)。但是,3在,因为2*3 <=10,所以5也在,2*5 2*5 <=10。

这个怎么算呢?

我们将下界取为2/2 (L//primes[i]) 但我们必须确保,我们不会再考虑当前的质数,因为(p和q一定是不同的),所以我们取最大的 L//primes[i]primes[i]+1.

对于2,我们的起始数是3(因为2+1=3,我们不能从1或2开始,因为如果考虑2,那么我们会计算出2*2=4这样的情况,是不成立的)。我们的末尾数是10/2=5,在3、5的范围内,质数中有多少个数。是2,通过简单的二进制搜索就可以找到。

其余的就简单了,我们要二进制搜索范围内有多少个质数。(max(L//primes[i], primes[i]+1), R//primes[i]).

这具有复杂度预处理时间复杂度为O(10^6*log(10^6))+O(10^6*log(10^6))和O(log(10^6))每次查询。

© www.soinside.com 2019 - 2024. All rights reserved.