PROBLEM LINK:
Practice
Contest: Division 1
Contest: Division 2
Contest: Division 3
Contest: Division 4
Author: raysh07
Tester: sushil2006
Editorialist: iceknight1093
DIFFICULTY:
Easy
PREREQUISITES:
Fenwick trees/segment trees
PROBLEM:
You’re given a permutation A.
Answer Q queries on it:
- Given L and R, create a new array B where B_i = \text{clamp}(A_i, L, R).
- Report the number of inversions in B.
EXPLANATION:
Consider two indices i and j, i \lt j.
Let’s analyze how their relative values change with respect to L and R.
- If A_i \lt A_j, then no matter what the values of L and R are, we’ll have B_i \leq B_j.
So, this pair of indices can never form an inversion. - If A_i \gt A_j, there are a few cases:
- If L \lt A_i, then B_i \geq B_j, with equality only when L = R.
- On the other hand, if L \geq A_i then we’ll have B_i = B_j = L.
- If R \gt A_j, again B_i \geq B_j, with equality only when L = R.
- If R \leq A_j, we’ll have B_i = B_j = R.
So, a pair of indices that don’t form an inversion in A, still won’t form an inversion in B.
A pair of indices that do form an inversion in A can either remain an inversion in B, or become a non-inversion.
In particular, given L and R, observe that the only inversions that become non-inversions are those with either both elements \leq L, or both elements \geq R.
Every other inversion will remain (except if L = R).
This in fact gives us a fairly simple solution.
Let \text{Inv} denote the number of inversions in A, \text{pre}_L denote the number of inversions in A when considering only elements \leq L, and \text{suf}_R denote the number of inversions when considering only elements \geq R.
The answer to query (L, R) is then just \text{Inv} - \text{pre}_L - \text{suf}_R.
As noted above, L = R is an edge case that doesn’t satisfy this: but for L = R the answer is trivially 0 since all elements will be equal.
Now, we need to compute the value of \text{Inv}, as well as every \text{pre}_L and \text{suf}_R.
Note that \text{Inv} = \text{pre}_N so that’s automatically handled by the latter anyway.
Let’s look at how the \text{pre}_L values can be computed. The \text{suf}_R values can be computed similarly.
First, we can initialize \text{pre}_L to \text{pre}_{L-1}, which takes care of all pairs with values \leq L-1.
We’re now left with only inversions involving L and an element smaller than L, which in turn is just the number of elements smaller than L which appear after it.
There are a couple of different ways of computing this.
For instance, one way is to maintain a “mark” array M, where M_i = 1 if A_i \leq L and M_i = 0 otherwise.
If we had such an array M, the value we’re looking for would just be M_{p+1} + M_{p+2} + \ldots + M_N, where p is the position of L, i.e. A_p = L.
Processing L in increasing order makes maintaining M trivial, since moving from L to L+1 just causes one element in M to change value from 0 to 1.
To do this quickly we need a data structure that supports point updates and range sum queries, which a fenwick tree or segment tree can handle.
Alternately, in C++ you can use order statistics trees which are luckily built in; other languages unfortunately don’t have this luxury.
TIME COMPLEXITY:
\mathcal{O}(N \log N + Q) per testcase.
CODE:
Editorialist's code (PyPy3)
# https://github.com/cheran-senthil/PyRival/blob/master/pyrival/data_structures/FenwickTree.py
class FenwickTree:
def __init__(self, x):
"""transform list into BIT"""
self.bit = x
for i in range(len(x)):
j = i | (i + 1)
if j < len(x):
x[j] += x[i]
def update(self, idx, x):
"""updates bit[idx] += x"""
while idx < len(self.bit):
self.bit[idx] += x
idx |= idx + 1
def query(self, end):
"""calc sum(bit[:end])"""
x = 0
while end:
x += self.bit[end - 1]
end &= end - 1
return x
for _ in range(int(input())):
n, q = map(int, input().split())
a = list(map(int, input().split()))
pos = [0]*(n+1)
for i in range(n): pos[a[i]] = i
pref, suf = [0]*(n+1), [0]*(n+2)
fen = FenwickTree([0]*n)
for i in range(1, n+1):
u = pos[i]
pref[i] = pref[i-1] + i-1-fen.query(u)
fen.update(u, 1)
fen = FenwickTree([0]*n)
for i in reversed(range(1, n+1)):
u = pos[i]
suf[i] = suf[i+1] + fen.query(u)
fen.update(u, 1)
while q > 0:
l, r = map(int, input().split())
if l == r: print(0)
else: print(pref[n] - pref[l] - suf[r])
q -= 1