なんか書かないと忘れちゃいそうなので。茶色の時に書けたし怖くないよ!でもいろいろ初心者なので間違ってたらどんどん言ってください!
BITについて
BinaryIndexedTree の略。
可換モノイド列 を乗せることができる。 (追記)
モノイドは、二項演算できて結合法則満たして単位元があればよい。可換であるためには交換法則を満たせばよい。(追記)まあたぶんモノイドじゃなかったら乗せるときに気づきそう。
しかし逆元がないと prefix sum しか求められません。(追記)可変群を扱うことが多いでしょう
クエリ
に以下の二つのクエリがどちらも でできる。
- :
- : を求める。
実装
ここ (Binary indexed tree) とかが強い。自分のブログで他人のスライド貼るのはどうなの
について、長さ の配列一個で表現できます。ただし、正確には長さ になります。( 1-indexed のほうが嬉しいので)
理論の図は上のスライドを見てください。 TODO:書く(絶対やらねえ)
個人的な実装の話
実装は 1-indexed のほうが適しているので見かけ上 0-indexed っぽくなるようにしています。
初めに をし、1-indexed のお気持ちになる。
その後、 を足していき、木を上に登っていく。
まず、 1-indexed にする。 をする。 半開区間 にそろえたいが実装上全開区間 ] のほうが都合がよいので、 をする。
すると、結局のところ のままで OK。
なので から を引き、木を下がっていく。
なお、 は最下位 bit を指す。 で求まるらしい。これ天才だろ。
BIT の実装例( C++ )
template<typename T> //0-indexed/内部的に 1-indexed struct BIT { vector<T> tree; //初期化 BIT(ll n) : tree(n) { tree.assign(n + 1, 0); } //[0, n) の sum を返す/0-indexed T sum(ll n) { T ret = 0; //実は 1-indexed だが半開区間なので見た目がそのまま for (; n >= 0; n -= LSB(n)) { ret += tree[n]; if (!n)break; } return ret; } //n 番目に x を足す void add(ll n, T x) { ll siz = tree.size(); for (++n; n < siz; n += LSB(n))tree[n] += x; } };
問題
RSQ( verify 用問題)
数列に対して加算と合計を聞くクエリにこたえる問題。
yosupo judge さん大好き!
judge.yosupo.jp
AOJ 版( 1-indexed であることに注意)
onlinejudge.u-aizu.ac.jp
ちなみに簡単な引き算で の合計は手に入る。
転倒数はバリ典型問題(以下に解法を載せます)
がとりうる値の長さ(制約より ) の配列 を取り、 を の出現回数とすると、 の前にいくつ大きい数があったかが で出る。これをもとの配列すべてについて回せばよく、また を回す前後で をすればよい。こうすることで今見ているところからの情報のみが得られる。
計算量は、クエリが , これを 回回すので 。
atcoder.jp
SegmentTree という BIT の上位互換のデータ構造があるのですが、それについては今度書きます。BIT は SegmentTree より実装が軽いのと定数倍が軽いのが特徴。
実装が軽いので、 BIT は二次元に発展させたりが簡単らしい。こういうのをやって記事にしたいね。