cp-library

競技プログラミング用 C++ ライブラリ

View the Project on GitHub qandori/cp-library

:heavy_check_mark: 区間の異なる値の個数(クエリ一括処理)

data_structure/offline_range_count_distinct.hpp · GitHub で見る

共通ページ

#include "lib/data_structure/offline_range_count_distinct.hpp"

OfflineRangeCountDistinct<int> query(std::vector<int>{2, 1, 2, 3});
query.add_query(0, 3);
query.add_query(1, 4);
auto answer = query.solve(); // {2, 3}
操作 内容・返り値
OfflineRangeCountDistinct<T,Hash,Equal>(values) / build(values) 初期列を設定し、登録済みの問い合わせを消去
size() 列の長さ
reserve(q) 問い合わせ数を予約
add_query(l,r) 半開区間 [l,r) の問い合わせを追加し、答えの添字を返す
solve() 異なる値の個数を登録順の vector<int> で返す

添字は 0 始まり、0 <= l <= r <= size() とします。空区間の答えは 0 です。solve() は登録内容を変更せず、繰り返し呼び出せます。入力配列は変更せず、構築後に破棄できます。T はコピー可能とし、Hash と Equal は std::unordered_map と同じ条件を満たすものを渡します。列長・問い合わせ数は INT_MAX 以下とします。

列長を \(N\)、問い合わせ数を \(Q\) とします。通常のハッシュ計算・比較を定数時間として、構築は \(O(N)\)、問い合わせの登録は償却 \(O(1)\)、solve() は期待 \(O((N+Q)\log N)\) 時間、空間は \(O(N+Q)\) です。

依存ライブラリ

検証コード

コード

#ifndef OFFLINE_RANGE_COUNT_DISTINCT_HPP
#define OFFLINE_RANGE_COUNT_DISTINCT_HPP

#include "flat_hash_map.hpp"
#include "../components/incremental_prefix_count.hpp"
#include <array>
#include <cassert>
#include <functional>
#include <limits>
#include <vector>

template <class T, class Hash = std::hash<T>, class Equal = std::equal_to<T>>
class OfflineRangeCountDistinct {
	std::vector<T> values;
	std::vector<std::array<int, 2>> queries;
  public:
	OfflineRangeCountDistinct() = default;
	explicit OfflineRangeCountDistinct(const std::vector<T> &a) { build(a); }
	void build(const std::vector<T> &a) {
		assert(a.size() <= std::size_t(std::numeric_limits<int>::max()));
		values = a; queries.clear();
	}
	int size() const { return int(values.size()); }
	void reserve(int count) { assert(count >= 0); queries.reserve(count); }
	int add_query(int l, int r) {
		assert(0 <= l && l <= r && r <= size() && queries.size() < std::size_t(std::numeric_limits<int>::max()));
		queries.push_back({l, r});
		return int(queries.size()) - 1;
	}
	std::vector<int> solve() const {
		std::vector<int> result(queries.size());
		if (queries.empty()) return result;
		std::vector<int> ends(std::size_t(size()) + 2), order(queries.size());
		for (auto query : queries) ++ends[std::size_t(query[1]) + 1];
		for (std::size_t i = 1; i < ends.size(); ++i) ends[i] += ends[i - 1];
		auto cursor = ends;
		for (int i = 0; i < int(queries.size()); ++i) order[cursor[queries[i][1]]++] = i;
		FlatHashMap<T, int, Hash, Equal> last;
		last.reserve(values.size());
		IncrementalPrefixCount positions(size());
		for (std::size_t r = 0; r <= values.size(); ++r) {
			if (r) {
				int &previous = last[values[r - 1]];
				positions.increment(previous);
				previous = r;
			}
			for (int i = ends[r]; i < ends[r + 1]; ++i) {
				int id = order[i];
				int l = queries[id][0];
				result[id] = std::size_t(l) == r ? 0 : positions.prefix(l + 1) - l;
			}
		}
		return result;
	}
};

#endif
Back to top page