trsing’s diary

勉強、読んだ本、仕事で調べたこととかのメモ。

ABC140 E - Second Sum用平衡二分探索木

//平行二分探索木
class BinarySearchTree<T> where T : IComparable<T>
{
    class node
    {
        public node left = null;
        public node right = null;
        public T key = default(T);
        public node parent = null;
        public int height;
    }
    node root = null;
    bool change;
    int height(node t) => t?.height ?? 0;
    int bias(node t) => height(t.left) - height(t.right);
    void modHeight(node t) => t.height = 1 + Math.Max(height(t.left), height(t.right));
    //vの左回転
    node rotateL(node v)
    {
        var u = v.right;
        var t2 = u.left;
        u.left = v; u.parent = v.parent; v.parent = u;
        if (u.parent != null)
            if (u.parent.right == v) u.parent.right = u;
            else u.parent.left = u;
        else
            root = u;
        v.right = t2; if (t2 != null) t2.parent = v;
        modHeight(u.left);
        modHeight(u);
        return u;
    }
    //uの右回転
    node rotateR(node u)
    {
        var v = u.left;
        var t2 = v.right;
        v.right = u;v.parent = u.parent;u.parent = v;
        if (v.parent != null)
            if (v.parent.right == u) v.parent.right = v;
            else v.parent.left = v;
        else
            root = v;
        u.left = t2;if (t2 != null) t2.parent = u;
        modHeight(v.right);
        modHeight(v);
        return v;
    }
    //tの二重回転(左→右)
    node rotateLR(node t)
    {
        t.left = rotateL(t.left);
        return rotateR(t);
    }
    //tの二重回転(右→左)
    node rotateRL(node t)
    {
        t.right = rotateR(t.right);
        return rotateL(t);
    }
    //挿入時の修正
    node balanceLi(node t) => balanceL(t);
    node balanceRi(node t) => balanceR(t);
    //削除時の修正
    node balanceLd(node t) => balanceR(t);
    node balanceRd(node t) => balanceL(t);
    //左部分木への挿入/右部分木での削除に伴う修正
    node balanceL(node t)
    {
        if (!change) return t;
        var h = height(t);
        if (bias(t) == 2)
        {
            if (0 <= bias(t.left))
                t = rotateR(t);
            else
                t = rotateLR(t);
        }
        else modHeight(t);
        change = (h != height(t));
        return t;
    }
    //右部分木への挿入/左部分木での削除に伴う修正
    node balanceR(node t)
    {
        if (!change) return t;
        var h = height(t);
        if (bias(t) == -2)
        {
            if (bias(t.right) <= 0)
                t = rotateL(t);
            else
                t = rotateRL(t);
        }
        else modHeight(t);
        change = (h != height(t));
        return t;
    }

    public BinarySearchTree() { }

    //挿入
    public void insert(T z)
    {
        change = true;
        _insert(new node() { key = z, height = 1 });
    }
    void _insert(node z)
    {
        node y = null;
        node x = root;
        while (x != null)
        {
            y = x;
            if (z.key.CompareTo(x.key) < 0)
                x = x.left;
            else
                x = x.right;
        }
        if (y == null)
        {
            root = z;
            change = false;
        }
        else if (z.key.CompareTo(y.key) < 0)
        {
            y.left = z;
            y.left.parent = y;
        }
        else
        {
            y.right = z;
            y.right.parent = y;
        }
        //挿入に伴う修正
        var s = z;
        while (s.parent != null)
        {
            if (s.parent.left == s)
                s = balanceLi(s.parent);
            else
                s = balanceRi(s.parent);
        }
    }
    //検索
    public bool find(T v)
    {
        var n = _find(v);
        return n == null ? false : true;
    }
    node _find(T v)
    {
        var x = root;
        while (x != null)
        {
            if (x.key.CompareTo(v) == 0)
                return x;
            if (x.key.CompareTo(v) < 0)
                x = x.right;
            else
                x = x.left;
        }
        return null;
    }

    //削除
    public void delete(T v) {
        change = true;
        _delete(_find(v));
    }
    void _delete(node n)
    {
        if (n == null)
            return;
        if (n == root)
        {
            change = false;
            if (n.left == null && n.right == null)
                root = null;
            else if (n.left != null && n.right == null)
            {
                root = n.left;
                root.parent = null;
            }
            else if (n.left == null && n.right != null)
            {
                root = n.right;
                root.parent = null;
            }
            else
            {
                var next = n.right;
                while (next.left != null)
                    next = next.left;
                n.key = next.key;
                change = true;
                _delete(next);
            }
            return;
        }
        var spl = n.parent.left;
        if (n.left == null && n.right == null)
        {
            if (n == n.parent.right)
                n.parent.right = null;
            else
                n.parent.left = null;
        }
        else if (n.left != null && n.right == null)
        {
            n.left.parent = n.parent;
            if (n == n.parent.right)
                n.parent.right = n.left;
            else
                n.parent.left = n.left;
        }
        else if (n.left == null && n.right != null)
        {
            n.right.parent = n.parent;
            if (n == n.parent.right)
                n.parent.right = n.right;
            else
                n.parent.left = n.right;
        }
        else
        {
            var next = n.right;
            while (next.left != null)
                next = next.left;
            n.key = next.key;
            _delete(next);
        }
        //削除に伴う修正
        var s = n;
        while (s.parent != null)
        {
            if (spl == s)
                s = balanceLd(s.parent);
            else
                s = balanceRd(s.parent);
            spl = s.parent?.left;
        }
    }

 
    //vより一つ前の値を得る
    public bool prev(T v, ref T r)
    {
        var t = _find(v);
        if (null == t)
            return false;
        if (null != t.left)
        {
            t = t.left;
            while (t.right != null)
                t = t.right;
            r = t.key;
            return true;
        }
        while (t.parent != null)
        {
            if (t.parent.right == t)
            {
                r = t.parent.key;
                return true;
            }
            t = t.parent;
        }
        return false;
    }
    //vより一つ後の値を得る
    public bool next(T v, ref T r)
    {
        var t = _find(v);
        if (null == t)
            return false;
        if (null != t.right)
        {
            t = t.right;
            while (t.left != null)
                t = t.left;
            r = t.key;
            return true;
        }
        while (t.parent != null)
        {
            if (t.parent.left == t)
            {
                r = t.parent.key;
                return true;
            }
            t = t.parent;
        }
        return false;
    }
}
参考

プログラミングコンテスト攻略のためのアルゴリズムとデータ構造

プログラミングコンテスト攻略のためのアルゴリズムとデータ構造

wwwa.pikara.ne.jp

背景

解説を読んで見て聞いて考え方はわかった。けど実装ができない。C++setみたいなのがあれば俺だってACできるのになー俺もなー。そんな経験はありませんか?ところで聞きました奥さん?setは二分木で実装されているんですって。というわけで作った。multisetが良かったけど今回はパス。

最初は「プログラミングコンテスト攻略のためのアルゴリズムとデータ構造」の二分探索木(平衡でない)使ったけど後半でTLE。これは平衡にしてやる必要がありますねぇということで解説がわかりやすいとこ見つけてパクった参考にしました。無理やり混ぜ合わせたので結構怪しい。中身を理解できてるかも怪しい。いや怪しくないきっぱりと理解できていない曖昧なとこなどみじんもない堂々と胸を張って生きろ。まあACできたからいいや。いやよくない。今日のところはこれで勘弁してやる。

他の人の提出見ると巷でセグ木と呼ばれている暗黒魔術らしきものを使ってるっぽいけどよくわからない。雰囲気でふわふわ生きてるので。このままふわふわお空の底まで昇っていければいいのに。あ、昇っていくなら佐世保がいいな。今艦これのリアイベ中だし。佐世保ちょっと遠すぎるのですよね。片道8hておまえ。転職で福岡に移住とかありかもしれない。あと目からビーム出るようにならないかな。そしたら私はうれしい。

おまけ

static void Main(string[] args)
{
    var N = Sarray()[0];
    var P = Sarray();
    var bst = new BinarySearchTree<long>();
    var idx = new long[N];
    for (long i = 0; i < N; ++i)
        idx[P[i] - 1] = i;

    long ans = 0L;
    for (var i = N - 1; 0 <= i; --i)
    {
        long ll = -1;
        long l = -1;
        long r = N;
        long rr = N;
        bst.insert(idx[i]);
        bst.prev(idx[i], ref l);
        bst.prev(l, ref ll);
        bst.next(idx[i], ref r);
        bst.next(r, ref rr);
        ans += (i + 1) * ((l - ll) * (r - idx[i]) + (rr - r) * (idx[i] - l));
    }
    WriteLine(ans);
}