【问题标题】:Counting inversion after swapping two elements of array交换数组的两个元素后计数反转
【发布时间】:2023-03-24 05:19:01
【问题描述】:

给定从 1 到 n 的数字排列 p1,p2,...,pn。

排列是长度为 n 的从 1 到 n 的整数序列,每个数字只包含一次。

给定 q 个查询,其中每个查询由两个整数 a 和 b 组成,作为对每个查询的响应,您需要在交换索引 a 和 b 处的元素后返回多个排列反转,这里每个查询都是独立的,即在每个查询的排列都会恢复到它的初始状态。

置换 p 中的反转是一对索引 (i, j),使得 i > j 和 pi 

输入:第一行包含 n,q。 第二行包含空格分隔的排列 p1,p2,...,pn。 接下来 q 行的每一行都包含两个整数 a,b。

输出:为每个查询打印一个整数,表示新行的反转次数。

Sample input: 
5 5
1 2 3 4 5
1 2
1 3
2 5
2 4
3 3

Output: 
1
3
5
3
0

Constraints: 
2<=n<=1000
1<=q<=200000

我的方法:在交换位置 a 和 b 的元素之后,我计算每个查询使用 BIT (https://www.geeksforgeeks.org/count-inversions-array-set-3-using-bit/) 的反转次数,然后再次交换它,以便我的数组保持不变。但是这个解决方案为大型测试用例提供了 TLE。有没有更好的办法解决这个问题?

【问题讨论】:

    标签: c++ algorithm


    【解决方案1】:

    您获得 TLE 可能是因为此方法中的计算次数为 q * (n * log(n)) = 2 * 10^5 * 10^3 * log(1000) = ~10^9,这比普遍接受的计算次数多 ~10^8。

    我可以想到以下解决方案。请注意,我尚未对其进行编码/验证:

    • 表示 ri == 索引数 j,例如 i &gt; j &amp;&amp; pi &lt; pj。例如:[2, 3, 1, 4], r3 = 2。基本上,它表示更远索引为i的反转次数。 (请注意,我根据问题使用基于 1 的索引。另外,a &lt; b 根据问题)

    • 因此我们有: r 的总和i == #invs(反转次数)

    • 我们可以计算O(n^2)中的初始总#invs

    • ab 互换时,我们可以观察到:

      a) ri 保持不变,其中i &lt; a .

      b) ri 保持不变,其中i &gt; b

    • 只有 ria &lt;= i &lt;=b 的位置发生变化,并且在以下这些条件下也会发生变化。我正在考虑pa &lt; pb 时的情况。 pa &gt; pb 时需要考虑完全相反的情况。

      a) 由于pa &lt; pb,因此此交换导致#invs = #invs + 1

      b) 如果(pi &lt; pa &amp;&amp; pi &lt; pb) || (pi &gt; pa &amp;&amp; pi &gt; pb),此交换不会更改 ri。例如:[2,....10,....5]。这里交换25 不会改变10r 值。

      c) 如果pa &lt; pi &lt; pb它将 ri 加 1,新的 rb 加 1。例如:[2,....3,.....4],当交换 2 和 4 时,我们有 [4,....3,....2]r3 增加 1(因为 4);并且r 的值2 也增加1(因为3)。请注意,由于what about 4 &gt; 2? 的增量已在步骤 (a) 中计算,只需执行一次。

      d) 我们需要找到所有这些指标i,其中pa &lt; pi &lt; pb 就像我们从上面开始的那样。让我们称之为f(a, b)。那么#invs 的总变化delta = (2 * f(a, b)) + 1,答案将是#original_invs + delta

    正如我所提到的,对于pa &gt; pb 案例,需要执行所有完全相反的步骤。在这种情况下,delta 将为负数。

    现在,唯一剩下的就是解决:给定a, b,高效地找到f(a, b)。为此,我们可以对所有索引对进行预处理和存储。这将占用O(N^2) 空间和O(N^2 * log(N)) 时间,使用平衡二叉搜索树(BST)。再次显示仅案例pa &lt; pb 的预处理步骤。另一种情况需要进行另一组预处理步骤:

    • 我们将使用自平衡BST,其中每个节点还包含以下字段:

      a) field_1:这表示左子树的大小。如果左子树的大小发生变化,该值将在每次插入操作时更新。

      b) field_2:这表示这棵树拥有的number of elements &lt; node.value。该值在插入节点时初始化一次,此后不再更改。我在附录 A 中添加了一个关于如何实现它的小解释。这个字段基本上就是我们的预处理,会确定f(a, b)

    • 现在,对于每个索引i,其中0 &lt;= i &lt; n,执行以下操作:创建新树。将 pj 值一一插入到树中,其中(i &lt; j &lt; n ) &amp;&amp; (pa &lt; pj) 。 (请注意,我们不会在pa &gt; pj 处插入值)。附录-A 中给出的方法将确保我们在插入时找到f(i, j)

    会有n 这样的预处理树,每个索引一个。查找f(a, b):我们需要查看ath 树,然后搜索node.value = pb。此节点的field_2 = f(a, b)

    插入的复杂度是O(logN)。因此,总的预处理计算 = O(N * N(logN))。搜索是O(logN),所以查询复杂度是O(q * logN)。总复杂度 = O(N^2) + O(N * N (logN)) + O(q * logN) 结果约为 10^7

    ================================================ ================================

    附录 A:如何在插入节点时填充 field_2:

         i) Insert the node, and balance the tree. Update field_1 as required.
    
         i) Initailze ans = 0. Traverse the BST from root searching for your node. 
    
         iii) do { 
                   If node.value < search_key_b, ans += node.left_subtree_size + 1
             } while(!node.found)
         iv) ans -= 1
    

    【讨论】:

      【解决方案2】:

      我们可以使用合并排序树在O(n log n) 空间和O(n log n + Q * log^2(n)) 时间解决这个问题。合并排序树允许我们找到子数组中大于或小于O(log^2(n)) 时间和O(n log n) 空间中的输入数的元素数。

      首先我们记录O(n log n)时间的反转总数,有已知的方法。要查询由leftright 绑定的交换的效果,请考虑subarray 之间的关系:

      subtract the number of elements greater
      than right in the subarray (those will
      no longer be inversions)
      
      subtract the number of elements smaller
      than left in the subarray (those will
      no longer be inversions)
      
      add the number of elements greater
      than left in the subarray (those will
      be new inversions)
      
      add the number of elements smaller
      than right in the subarray (those will
      be new inversions)
      
      if right > left, add 1
      if left > right, subtract 1
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-05-22
        • 2021-10-16
        • 1970-01-01
        • 2017-01-30
        相关资源
        最近更新 更多