読者です 読者をやめる 読者になる 読者になる

うにゅーん、って感じだ

SRM, CF, AtCoder黄. SRM highest:1746. C#を書きます.

Segment treeを整備したという日記

C#

このブログの存在をすっかり忘れていた話。



最近Sumsegtreeを使う問題を解いたら、持ってたSumsegtreeがバグってたので、ついでにもっと色々使えるように手直ししたのでその備忘録みたいなものを書きます。
c#でのsegtreeのコード載せてるページ少ないし、ね。


なお、基本的に秋葉大先生のスライドをとても参考にさせていただいて作っているので俺の記事は読まなくていいのでこちらをお読みください
プログラミングコンテストでのデータ構造


以下コードです。

using System;

class Segtree<T>
{
    int n;
    T[] tree;
    Func<T, T, T> f;
    T exnum;
    public Segtree(int m, Func<T, T, T> f, T ex)
    {
        this.f = f;
        this.exnum = ex;
        n = 1;
        while (n < m) n <<= 1;

        tree = new T[(n << 1) - 1];
        for (int i = 0; i < tree.Length; i++) tree[i] = ex;
    }
    public Segtree(int m, T ini, Func<T, T, T> f, T ex) : this(m, f, ex)
    {
        for (int i = 0; i < m; ++i) tree[i + n - 1] = ini;
        all_update();
    }
    public void assign_without_update(int j, T x)
    {
        tree[j + n - 1] = x;
    }
    public void update(int j, T x)
    {
        int i = j + n - 1;
        tree[i] = x;
        while (i > 0)
        {
            i = (i - 1) >> 1;
            tree[i] = f(tree[(i << 1) + 1], tree[(i << 1) + 2]);
        }
    }
    public void all_update()
    {
        for (int i = n - 2; i >= 0; i--)
            tree[i] = f(tree[(i << 1) + 1], tree[(i << 1) + 2]);
    }
    public T look(int i) { return tree[i + n - 1]; }

    // [s, t)
    public T run(int s, int t) { return query(s, t, 0, 0, n); }
    T query(int s, int t, int k, int l, int r)
    {
        if (r <= s || t <= l) return exnum;
        if (s <= l && r <= t) return tree[k];

        return f(query(s, t, (k << 1) + 1, l, (l + r) >> 1), query(s, t, (k + 1) << 1, (l + r) >> 1, r));
    }
}

(2016/04/19 : iniが指定されていない時にexnumで初期化するように設定しました)
(2016/10/03 : 色々細かいところを修正したのと、全体の初期化を {O(N \log N)} でなく {O(N)} で実現するための関数を追加、あと半開区間にしました!)

まず、型を自由に指定できるようにしてあります、これは基本的にはlongにしとけばどうにかなるのであんま意味ない気もしますが、doubleにしたい時とか
D: タコヤキオイシクナール - AtCoder Regular Contest 008 | AtCoder
とかだとまぁ楽かなぁという気がします。


それでは、変数の解説をしていくと、

・Func f
Func f は更新操作で用いる関数です。例えば、Sumsegtreeにしたい時は、ラムダ式で (i, j) => i + j などと与えてあげれば良いです。
また、MaxとかMinだと Math.Max とか Math.Min を渡してあげるだけで動きます、嬉しい。
その他でも、f(x1, f(x2, x3)) = f(f(x1, x2), x3) を満たす関数なら動くのかな...? 理学部じゃないのでわからん。

・T exnum
exnum は、区間が範囲外の時に返す値を入れる変数で、とても弱い値を入れておけばokです。例えば、Sumの時は0, Maxの時は-∞, Minの時は+∞, といったように。

・T ini
ini は、初期化用変数です。別にいらないような気もするかもですが、Tがclassだったりすると馬鹿みたいにランタイムエラーを起こすのであったほうが良いです。


あとは前述したスライドとほぼ同じなので、これで使えます。
一番の肝はコンストラクタで初期化する時で、そこがちゃんと出来てしまえばあとは毎回同じように使えます。めっちゃ嬉しい。


こちらが使用例になります。
Submission #694898 - AtCoder Regular Contest 033 | AtCoder
Submission #694924 - AtCoder Regular Contest 008 | AtCoder
#118362 No.426 往復漸化式 - yukicoder
(2016/10/03 : 使用例を追加しました)


最小費用流とか平衡二分探索木とかも持っとかなきゃなぁという気はしてる(書く気は起きない)。