您获得 TLE 可能是因为此方法中的计算次数为 q * (n * log(n)) = 2 * 10^5 * 10^3 * log(1000) = ~10^9,这比普遍接受的计算次数多 ~10^8。
我可以想到以下解决方案。请注意,我尚未对其进行编码/验证:
-
表示 ri == 索引数 j,例如 i > j && pi < pj。例如:[2, 3, 1, 4], r3 = 2。基本上,它表示更远索引为i的反转次数。 (请注意,我根据问题使用基于 1 的索引。另外,a < b 根据问题)
-
因此我们有: r 的总和i == #invs(反转次数)
-
我们可以计算O(n^2)中的初始总#invs
-
当a 和b 互换时,我们可以观察到:
a) ri 保持不变,其中i < a .
b) ri 保持不变,其中i > b。
-
只有 ri 在a <= i <=b 的位置发生变化,并且在以下这些条件下也会发生变化。我正在考虑pa < pb 时的情况。 pa > pb 时需要考虑完全相反的情况。
a) 由于pa < pb,因此此交换导致#invs = #invs + 1
b) 如果(pi < pa && pi < pb) || (pi > pa && pi > pb),此交换不会更改 ri。例如:[2,....10,....5]。这里交换2 和5 不会改变10 的r 值。
c) 如果pa < pi < pb,它将 ri 加 1,新的 rb 加 1。例如:[2,....3,.....4],当交换 2 和 4 时,我们有 [4,....3,....2],r 值 3 增加 1(因为 4);并且r 的值2 也增加1(因为3)。请注意,由于what about 4 > 2? 的增量已在步骤 (a) 中计算,只需执行一次。
d) 我们需要找到所有这些指标i,其中pa < pi < pb 就像我们从上面开始的那样。让我们称之为f(a, b)。那么#invs 的总变化delta = (2 * f(a, b)) + 1,答案将是#original_invs + delta。
正如我所提到的,对于pa > pb 案例,需要执行所有完全相反的步骤。在这种情况下,delta 将为负数。
现在,唯一剩下的就是解决:给定a, b,高效地找到f(a, b)。为此,我们可以对所有索引对进行预处理和存储。这将占用O(N^2) 空间和O(N^2 * log(N)) 时间,使用平衡二叉搜索树(BST)。再次显示仅案例pa < pb 的预处理步骤。另一种情况需要进行另一组预处理步骤:
-
我们将使用自平衡BST,其中每个节点还包含以下字段:
a) field_1:这表示左子树的大小。如果左子树的大小发生变化,该值将在每次插入操作时更新。
b) field_2:这表示这棵树拥有的number of elements < node.value。该值在插入节点时初始化一次,此后不再更改。我在附录 A 中添加了一个关于如何实现它的小解释。这个字段基本上就是我们的预处理,会确定f(a, b)。
-
现在,对于每个索引i,其中0 <= i < n,执行以下操作:创建新树。将 pj 值一一插入到树中,其中(i < j < n ) && (pa < pj) 。 (请注意,我们不会在pa > 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