文書の過去の版を表示しています。
転倒数
具体的な例でイメージすると、$\{3,10,1,8,5\}$ のような数列を昇順にソートする時、「正しい順序になっていないペアの数」のことである。
これは、隣り合う2数の交換を繰り返すことでソートするバブルソートにおいて、その必要な最小回数と一致する。
例の場合、$(3,1)(10,1)(10,8)(10,5)(8,5)$ の5つの組で、左にある数字が右より大きくなっているので、転倒数は5である。
実際にやってみると、
3 10 1 8 5 3 1 10 8 5 3 1 8 10 5 3 1 8 5 10 1 3 8 5 10 1 3 5 8 10
たしかに5回で並び替えられる。
転倒数の求め方
数列の各要素について、独立に考えることが出来る。
つまり、「自分より右にある、自分より小さな数の個数」を、全要素求めて足し合わせると、それが転倒数となる。
3→ 10 1 8 5 : 1 の 1つ 10→ 1 8 5 : 1,8,5 の 3つ 1→ 8 5 : なし 8→ 5 : 5 の 1つ 5→ : なし 1+3+1 = 5
また、「自分より左にある、自分より大きな数の個数」でも同じ事である。
3 10 1 8 ←5 : 10,8 の 2つ 3 10 1 ←8 : 10 の 1つ 3 10 ←1 : 3,10 の 2つ 3 ←10 : なし ←3 : なし
「自分より左にある、自分より大きな数の個数」を高速に求めるには、 Binary Indexed Tree を用いる方法が使われる。
BITの詳細は上記リンクを参照。以下の処理を高速に行うデータ構造である。
- 数列$\{a_1,a_2,...,a_n\}$があり、最初、全ての要素は0である
- 以下の2つの要求を処理する
ADD(i, x)
: $a_i$に$x$を加算($1 \le i \le n$)SUM(t)
: $a_1$から$a_t$までの合計を得る($1 \le t \le n$)
これを用いて、以下のように算出する。
BITを用意
まず、数列の最大値以上のサイズのBITを用意する。最大値がわかっていることが前提だが、だいたいは制約条件がある。
数列を先頭から順番に処理する
$i$ 番目(1-indexed)の項を $p_i$ とする。各項に付き、以下の2つの処理を行う。
BITに加算する
$ADD(p_i, 1)$(BITの $p_i$ の位置に、1を加算する)
自分より左にある自分より大きい数の個数を求める
先頭から順番に処理しているので、今BITにあるのは「自分より左に各数がいくつあるか」の情報である。
自分より左で $p_i$ 以下の数の個数は、$SUM(p_i)$(BITで $1$~$p_i$ の和)を求めることで得られる。
逆に言うと、$p_i$ より大きい数の個数は、$i - SUM(p_i)$ である。
数列の各要素につきこれを足し合わせることで、転倒数が得られる。
class Bit: def __init__(self, n): self.size = n self.tree = [0] * (n + 1) def sum(self, i): s = 0 while i > 0: s += self.tree[i] i -= i & -i return s def add(self, i, x): while i <= self.size: self.tree[i] += x i += i & -i ppp = [3, 10, 1, 8, 5] bit = Bit(16) ans = 0 for i, p in enumerate(ppp): bit.add(p, 1) ans += i + 1 - sum(p) print(ans)