【问题标题】:Maximum number not coprime to V最大数不与 V 互质
【发布时间】:2014-03-13 10:48:06
【问题描述】:

给定一个包含 N 个整数的固定数组 A,其中 N

现在我有多达 100,000 个 {V, L, R} 形式的查询,在每个查询中我需要找到最大的数 A[i],其中 i 在 [L,R] 范围内不是互质的给定值V。(即GCD(V,A[i])不等于1。)

如果不可能,那么还要告诉给定范围内的所有数字都与 V 互质。

一种基本的方法是从 L 和 R 之间的每个 A[i] 进行迭代,并用值 V 计算 GCD,从而找到最大值。但是如果查询的数量也可以达到 100,000,有没有更好的方法来做到这一点。在这种情况下,每次都检查每个数字的效率太低了。

例子:

  • 设 N=6,数组为 [1,2,3,4,5,4],V 为 2,范围 [L,R] 为 [2,5]。
  • 那么答案是 4。

解释:

GCD(2,2)=2
GCD(2,3)=1
GCD(2,4)=2
GCD(2,5)=1

所以这里的最大值是 4。

【问题讨论】:

  • 这是网评问题吗?可以发个链接吗?
  • 它来自正在运行的 codechef 挑战。我会通过在此处发布解决方案来阻止用户破坏乐趣
  • 尝试使用段树+RMQ
  • @TejasPatel 如何在这个问题中使用 RMQ?
  • 我知道这是一个竞赛问题,所以我现在不想讨论它。

标签: c++ algorithm


【解决方案1】:

由于您有一个大数组,但只有一个 V,因此从分解 V 开始应该更快。之后,您的互质测试就变成了简单地找到以 V 的每个唯一因子为模的余数。

【讨论】:

  • 但是每个查询都会有一个不同的 V。您能详细说明一下您的方法吗?还有您所说的“找到 V 的每个唯一因子的余数”是什么意思。
  • @user3405426:我的意思是,如果V = pow(2,17) * 3; 代替GCD(n, V) == 1,您可以测试n % 2 == 0 && n % 3 == 0,这比GCD 和短路都简单。您需要为每个查询运行一次分解,这比每个数组元素一次要好得多。
  • 一个算法会很有帮助。我不会让你喜欢这样。但是你想让我检查范围内的每个元素吗?这并不比简单地检查每个元素的 GCD 更好
  • @user3405426:回到互素的定义。这意味着没有n 的因子也是V 的因子。显然,对于任何nV 的辅因子kk 的所有因子也是辅因子。所以只需要考虑素数,无需重复。它会短路——只要发现V任何 因子除以n,你就知道它们不是互质的。
  • @user3405426 - O(log V),但实际上: floor(lg(V)),或 (V) 的有效位数 - 1。因为 2 是最小的素数,而 2^ 16 = 65536 distinct 素因子。乘积 10^5。
【解决方案2】:

Daniel Bernstein 的 "Factoring into coprimes in essentially linear time"(Journal of Algorithms 54:1, 1-30 (2005))回答了类似的问题,并被 Nadia Heninger 的 "New research: There's No Need to Panic Over Factorable Keys--Just Mind Your Ps and Qs"` 用于识别不良(重复因子)RSA 模数。问题是要找到大量非常大的数字之间的公因数,而不是一次找到一对。

【讨论】:

  • @user3405426,找到因素很重要的。并且 Pollard rho 不能在 O(log n) 时间内准确工作。
【解决方案3】:

让我们这么说

V = p_1*...*p_n

其中 p_i 是一个素数(您可以将其限制为仅不同的素数)。现在答案是

result = -1
for p_i:
    res = floor(R / p_i) * p_i
    if res >= L and res > result:
        result = res

因此,如果您可以快速分解V,那么这将非常有效。

编辑我没有注意到数组不必包含所有整数。在这种情况下对其进行筛分,即给定素数 p_1, ..., p_n 创建一个“反转”筛子(即范围 [L, R] 内的所有素数倍数)。然后,您可以将该筛子与您的初始数组相交。

EDIT2要生成所有倍数的集合,您可以使用以下算法:

primes = [p_1, ..., p_n]
multiples = []
for p in primes:
    lower = floor(L / p)
    upper = floor(R / p)
    for i in [lower+1, upper]:
        multiples.append(i*p)

重要的是,从数学中可以得出V[L, R] 范围内的每个 数字互质,而multiples 范围内的数字则不存在。现在你只需这样做:

solution = -1
for no in initial_array:
    if no in multiples:
        solution = max(solution, no)

请注意,如果您将result 实现为一个集合,则if no in result: 检查是O(1)

示例假设V = 6 = 2*3initial_array = [7,11,12,17,21]L=10R=22。让我们从倍数开始。按照算法我们得到了

multiples = [10, 12, 14, 16, 18, 20, 22, 12, 15, 18, 21]

前 7 个是 2 的倍数(范围 [10, 22]),后 4 个是 3 的倍数(范围 [10, 22])。由于我们正在处理集合(std::set?),因此不会有重复项(12 和 18):

multiples = [10, 12, 14, 16, 18, 20, 22, 15, 21]

现在通过initial_array 并检查multiples 中的值。我们得到最大的这样的数字是21。事实上216 不互质。

【讨论】:

  • 这里的L是什么。范围的下限?我不明白你写了什么,请说清楚
  • 我认为你误解了这个问题。我需要从数组中找到不与 V 互质的最大值
  • @user3405426 是的,[L, R] 是范围。我已经重构了伪代码。
  • @freakish:似乎并非[L,R] 范围内的所有数字都是候选数字,只有出现在数组中的数字才是候选数字。
【解决方案4】:

分解 A 的每个元素,并为每个可能的质因数存储包含该因数的数字的排序列表。

给定一个数字 n 包含 O(log n) 个质因数,这个列表将使用 O(N log N) 个内存。

然后,对于每个查询 (V, L, R),搜索 V 中的每个素数因子,在 [L, R] 中包含该因子的最大数是多少(这可以通过简单的二分搜索来完成) .

【讨论】:

  • 请提供一些算法或示例。我认为您的解决方案比其他解决方案更好。
  • 我没有理解你的行“分解每个 A 的元素,并为每个可能的质因数存储包含该因数的数字的排序列表。”
猜你喜欢
  • 1970-01-01
  • 2023-03-17
  • 2016-05-12
  • 1970-01-01
  • 2012-02-24
  • 1970-01-01
  • 2020-05-03
  • 1970-01-01
  • 2010-11-06
相关资源
最近更新 更多