Thanks to visit codestin.com
Credit goes to hitonanode.github.io

cplib-cpp

This documentation is automatically generated by online-judge-tools/verification-helper

View the Project on GitHub hitonanode/cplib-cpp

:heavy_check_mark: Offline dynamic connectivity
(data_structure/offline_dynamic_connectivity.hpp)

オフライン版の dynamic connectivity (の抽象化実装).

以下の性質を満たす何らかのオブジェクト $x$ と,$x$ に対するさまざまな作用たち $f_i$ を考えたい:

このとき、本コードは以下のオフラインクエリ群の高速処理をサポートする.

汎用化のため具体的なオブジェクト $x$ や作用 $f$ たちの管理は本コードでは行っておらず,実装者が UnionFind などを別途用意する必要がある.

原理

入力に登場する時刻 $t$ を列挙し二分木を構築する.作用クエリをセグメント木の要領で頂点に効率的に配置し,二分木を DFS しながら作用の演算・undo 操作・特徴量取得を行えばよい.

使用方法

// Tick の型は std::lower_bound 等による二分探索ができればなんでもよい(std::pair, std::tuple なども可)
offline_dynamic_connectivity<int> dc;

dc.apply_time_range(0, 100, 1);  // 時刻 [0, 100) の範囲で辺番号 1 を追加
dc.add_observation(50, 2);  // 時刻 50 に取得クエリ 2 を追加

for (auto p : dc.generate()) {
    if (p.op == DyConOperation::Begins) {
        int edge_id = p.id_;
        // 辺番号 edge_id を張る
    } else if (p.op == DyConOperation::Ends) {
        // undo 操作を一回行う
    } else if (p.op == DyConOperation::Event) {
        int query_id = p.id_;
        // 取得クエリ query_id を行う
    }
}

具体例:無向グラフの連結成分に関する情報取得

オブジェクト $x$ を $n$ 頂点の無向グラフとして,作用として

の 2 つ,特徴量として「連結成分の頂点重み和」を考えると,これは undo 可能 UnionFind で効率的に処理できる.よって本ライブラリを利用することで高速に解ける(「問題例」の “Dynamic Graph Vertex Add Component Sum” が該当).

問題例

Verified with

Code

#pragma once
#include <algorithm>
#include <limits>
#include <set>
#include <utility>
#include <vector>

enum class DyConOperation {
    Begins = 1,
    Ends = 2,
    Event = 3,
};

template <class Tick = int> struct offline_dynamic_connectivity {

    std::vector<std::pair<Tick, int>> ops;

    struct Edge {
        Tick since;
        Tick until;
        int edge_id;
    };
    std::vector<Edge> edges;

    offline_dynamic_connectivity() = default;

    void add_observation(Tick clk, int event_id) { ops.emplace_back(clk, event_id); }

    void apply_time_range(Tick since, Tick until, int edge_id) {
        edges.push_back(Edge{since, until, edge_id});
    }

    struct Procedure {
        DyConOperation op;
        int id_;
    };

    std::vector<std::vector<Procedure>> nodes;
    std::vector<Procedure> ret_;

    void rec(int now) {
        ret_.insert(ret_.end(), nodes[now].cbegin(), nodes[now].cend());
        if (now * 2 < int(nodes.size())) rec(now * 2);
        if (now * 2 + 1 < int(nodes.size())) rec(now * 2 + 1);

        for (auto itr = nodes[now].rbegin(); itr != nodes[now].rend(); ++itr) {
            if (itr->op == DyConOperation::Begins) {
                ret_.push_back(Procedure{DyConOperation::Ends, itr->id_});
            }
        }
    }

    std::vector<Procedure> generate() {
        if (ops.empty()) return {};

        std::vector<Tick> query_ts;
        {
            query_ts.reserve(ops.size());
            for (const auto &p : ops) query_ts.push_back(p.first);
            std::sort(query_ts.begin(), query_ts.end());
            query_ts.erase(std::unique(query_ts.begin(), query_ts.end()), query_ts.end());

            std::vector<int> t_use(query_ts.size() + 1);
            t_use.at(0) = 1;

            for (const Edge &e : edges) {
                t_use[std::lower_bound(query_ts.begin(), query_ts.end(), e.since) - query_ts.begin()] =
                    1;
                t_use[std::lower_bound(query_ts.begin(), query_ts.end(), e.until) - query_ts.begin()] =
                    1;
            }
            for (int i = 1; i < int(query_ts.size()); ++i) {
                if (!t_use[i]) query_ts[i] = query_ts[i - 1];
            }

            query_ts.erase(std::unique(query_ts.begin(), query_ts.end()), query_ts.end());
        }

        const int N = query_ts.size();
        int D = 1;
        while (D < int(query_ts.size())) D *= 2;

        nodes.assign(D + N, {});

        for (const Edge &e : edges) {
            int l =
                D + (std::lower_bound(query_ts.begin(), query_ts.end(), e.since) - query_ts.begin());
            int r =
                D + (std::lower_bound(query_ts.begin(), query_ts.end(), e.until) - query_ts.begin());

            while (l < r) {
                if (l & 1) nodes[l++].push_back(Procedure{DyConOperation::Begins, e.edge_id});
                if (r & 1) nodes[--r].push_back(Procedure{DyConOperation::Begins, e.edge_id});
                l >>= 1, r >>= 1;
            }
        }

        for (const auto &op : ops) {
            int t = std::upper_bound(query_ts.begin(), query_ts.end(), op.first) - query_ts.begin();
            nodes.at(t + D - 1).push_back(Procedure{DyConOperation::Event, op.second});
        }
        ret_.clear();
        rec(1);
        return ret_;
    }
};
#line 2 "data_structure/offline_dynamic_connectivity.hpp"
#include <algorithm>
#include <limits>
#include <set>
#include <utility>
#include <vector>

enum class DyConOperation {
    Begins = 1,
    Ends = 2,
    Event = 3,
};

template <class Tick = int> struct offline_dynamic_connectivity {

    std::vector<std::pair<Tick, int>> ops;

    struct Edge {
        Tick since;
        Tick until;
        int edge_id;
    };
    std::vector<Edge> edges;

    offline_dynamic_connectivity() = default;

    void add_observation(Tick clk, int event_id) { ops.emplace_back(clk, event_id); }

    void apply_time_range(Tick since, Tick until, int edge_id) {
        edges.push_back(Edge{since, until, edge_id});
    }

    struct Procedure {
        DyConOperation op;
        int id_;
    };

    std::vector<std::vector<Procedure>> nodes;
    std::vector<Procedure> ret_;

    void rec(int now) {
        ret_.insert(ret_.end(), nodes[now].cbegin(), nodes[now].cend());
        if (now * 2 < int(nodes.size())) rec(now * 2);
        if (now * 2 + 1 < int(nodes.size())) rec(now * 2 + 1);

        for (auto itr = nodes[now].rbegin(); itr != nodes[now].rend(); ++itr) {
            if (itr->op == DyConOperation::Begins) {
                ret_.push_back(Procedure{DyConOperation::Ends, itr->id_});
            }
        }
    }

    std::vector<Procedure> generate() {
        if (ops.empty()) return {};

        std::vector<Tick> query_ts;
        {
            query_ts.reserve(ops.size());
            for (const auto &p : ops) query_ts.push_back(p.first);
            std::sort(query_ts.begin(), query_ts.end());
            query_ts.erase(std::unique(query_ts.begin(), query_ts.end()), query_ts.end());

            std::vector<int> t_use(query_ts.size() + 1);
            t_use.at(0) = 1;

            for (const Edge &e : edges) {
                t_use[std::lower_bound(query_ts.begin(), query_ts.end(), e.since) - query_ts.begin()] =
                    1;
                t_use[std::lower_bound(query_ts.begin(), query_ts.end(), e.until) - query_ts.begin()] =
                    1;
            }
            for (int i = 1; i < int(query_ts.size()); ++i) {
                if (!t_use[i]) query_ts[i] = query_ts[i - 1];
            }

            query_ts.erase(std::unique(query_ts.begin(), query_ts.end()), query_ts.end());
        }

        const int N = query_ts.size();
        int D = 1;
        while (D < int(query_ts.size())) D *= 2;

        nodes.assign(D + N, {});

        for (const Edge &e : edges) {
            int l =
                D + (std::lower_bound(query_ts.begin(), query_ts.end(), e.since) - query_ts.begin());
            int r =
                D + (std::lower_bound(query_ts.begin(), query_ts.end(), e.until) - query_ts.begin());

            while (l < r) {
                if (l & 1) nodes[l++].push_back(Procedure{DyConOperation::Begins, e.edge_id});
                if (r & 1) nodes[--r].push_back(Procedure{DyConOperation::Begins, e.edge_id});
                l >>= 1, r >>= 1;
            }
        }

        for (const auto &op : ops) {
            int t = std::upper_bound(query_ts.begin(), query_ts.end(), op.first) - query_ts.begin();
            nodes.at(t + D - 1).push_back(Procedure{DyConOperation::Event, op.second});
        }
        ret_.clear();
        rec(1);
        return ret_;
    }
};
Back to top page