【问题标题】:Sum of xor of digits of all numbers from 1 to N when N is very large当 N 很大时,从 1 到 N 的所有数字的 xor 和
【发布时间】:2016-12-17 08:33:10
【问题描述】:

给定 1 和 N ,我们需要找到从 1 到 N 的所有数字的 XOR 值之和, 例如对于 N ,我们需要计算函数 F

F(k){
    ans =0;
    while(k>0){
      ans= ans^(k%10);
      k/=10;
   }
   return ans;
}

对于 [1,N] 中的 K。

有没有一种有效的方法可以说 . 据我所知,我们应该计算值 V 使用 DP 产生 XOR 的次数,但我想不出实现它的方法。

请给出一些想法。

示例:F (37) = 3 ^ 7 = 4

注意:N 可以大到 10^18

【问题讨论】:

  • 你确定它是十进制数字的异或,而不是二进制数字的异或吗?
  • 我添加了一个例子
  • 您真正需要知道的是范围 1..N 中每个数字 (0..9) 的出现次数是奇数还是偶数。
  • 您只需计算该组数字中每个数字重复的次数。排除偶数位数,异或奇数位数

标签: algorithm dynamic-programming


【解决方案1】:

天真的方法需要O(N*log10(N)) 时间,通过做一个简单的优化你可以得到O(N) 运行时。这个想法是:

一个 k 位数字的所有数字的 XOR = 前 (k - 1) 位数字的 XOR ^ 最后一个数字。

显然,您可以通过将数字除以 10 找到前 k - 1 个数字,因此关系变为:

F(i) = F(i / 10) ^ (i % 10)

代码:

result = 0
for i = 1:N
  dp[i] = dp[i/10] ^ i%10
  result += dp[i]

【讨论】:

  • 但是N可以大到10^18
  • 这与问题中的示例有何不同?
  • @user269985 哦,那么由于内存限制,它将无法工作:(
  • @SomeWittyUsername 计算单个数字的 F(i) 需要 O(1) 操作。在示例中,需要 O(log10(i)) 次操作来完成相同的操作。
  • 您在这里混淆了数字和数字。 N是数字上的边界
【解决方案2】:
  • 让我们计算一个稍微不同的事情:对于所有X from 0 to 15,从 1 到 N 有多少个数字的 xor 等于 X

  • 一旦我们知道,您问题的答案就是所有有效Xcount[X] * X 的总和。

  • 我们如何才能有效地计算它?我们将使用标准的逐位动态规划

  • 我们可以将所有数字视为字符串。如果我们用零填充它们,使它们的长度等于N 中的位数,我们可以按字典顺序比较它们。

  • 动态规划的状态是:(prefixLength, curXor, isSmaller) - 放置第一个 prefixLength 最高有效数字的方式数,其异或为 curXor。如果此前缀小于N 的前缀,则isSmaller 为真。否则前缀等于N

  • 转换是多放一位数字。 curXor 的变化微乎其微。 prefixLength 加一。如果isSmaller 为真,我们可以放置任何数字并继续。否则,我们不能在N 中放置比此位置的数字更大的数字。如果我们放同一个,isSmaller 保持错误。否则就成真了。

  • LN 中的位数。在[1, N] 范围内,恰好有f(L, X, 0) + f(L, X, 1) 的数字异或等于X

  • 我们完成了!状态数大约是 16 * L * 2,转换数大约是 16 * L * 2 * 10。作为L = O(log N),这个解决方案的时间复杂度是O(log N),看起来还不错。

这个解决方案背后的想法是标准的:当我们需要计算给定范围内有多少数字具有特定属性(如数字总和、异或或其他数字的函数)时,我们将数字逐一放置-从最重要到最不重要的一个,并跟踪3个参数:(已放置的位数,是小于N的前缀,一些特定于问题的数据)。

【讨论】:

  • @user269985 F#: let g n = let rec f n = let arr = Array.zeroCreate<bigint> 16 in if n = 0I then arr, 0 else let d, r = bigint.DivRem(n, 10I) in let a, s = f d in begin for j = 0 to 15 do for i = 0 to 9 do arr.[j ^^^ i] <- arr.[j ^^^ i] + a.[j] done done; for i = 0 to int r - 1 do arr.[s ^^^ i] <- arr.[s ^^^ i] + 1I done; arr, s ^^^ int r end in n + 1I |> f |> fst |> Array.mapi (fun i c -> bigint i * c) |> Array.sum in g (bigint.Pow(10I, 100))
猜你喜欢
  • 1970-01-01
  • 2019-06-01
  • 2016-02-07
  • 2020-10-02
  • 2013-02-21
  • 2019-06-20
  • 2022-11-17
  • 2022-08-06
  • 2021-08-14
相关资源
最近更新 更多