Skip to content

Commit 375ee55

Browse files
authored
Merge pull request #368 from hitonanode/hilbert-order-mos-3d
3D Mo with Hilbert order
2 parents af3a6fc + 2f0bf88 commit 375ee55

File tree

2 files changed

+172
-0
lines changed

2 files changed

+172
-0
lines changed
+49
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
#pragma once
2+
#include <cassert>
3+
#include <limits>
4+
#include <tuple>
5+
#include <type_traits>
6+
#include <utility>
7+
8+
// Encode 3D point (x, y, z) to Hilbert order
9+
// Implementation based on https://arxiv.org/abs/2308.05673
10+
template <class Uint = unsigned long long> Uint encode_hilbert_order_3d(Uint x, Uint y, Uint z) {
11+
static_assert(std::is_unsigned_v<Uint>);
12+
static_assert(std::numeric_limits<Uint>::digits >= 64);
13+
auto update = [](Uint x, Uint y, Uint z, unsigned w, unsigned o) -> std::tuple<Uint, Uint, Uint> {
14+
if (o == 0) return {z, x, y};
15+
if (o == 1) return {y, z, x - w};
16+
if (o == 2) return {y, z - w, x - w};
17+
if (o == 3) return {w - x - 1, y, 2 * w - z - 1};
18+
if (o == 4) return {w - x - 1, y - w, 2 * w - z - 1};
19+
if (o == 5) return {2 * w - y - 1, 2 * w - z - 1, x - w};
20+
if (o == 6) return {2 * w - y - 1, w - z - 1, x - w};
21+
if (o == 7) return {z, w - x - 1, 2 * w - y - 1};
22+
assert(false);
23+
};
24+
25+
int rmin = 1;
26+
while ((x | y | z) >> rmin) ++rmin;
27+
28+
assert(rmin * 3 <= (int)sizeof(Uint) * 8);
29+
30+
const int t = (-rmin % 3 + 3) % 3;
31+
if (t == 1) {
32+
std::swap(y, z);
33+
std::swap(x, y);
34+
} else if (t == 2) {
35+
std::swap(x, y);
36+
std::swap(y, z);
37+
}
38+
39+
Uint h = 0;
40+
41+
for (Uint w = Uint(1) << (rmin - 1); w; w >>= 1) {
42+
bool bx = (x >= w), by = (y >= w), bz = (z >= w);
43+
unsigned o = (bx ^ by ^ bz) + 2 * (by ^ bz) + 4 * by;
44+
h = 8 * h + o;
45+
std::tie(x, y, z) = update(x, y, z, w, o);
46+
}
47+
48+
return h;
49+
}
+123
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
---
2+
title: 3D points to Hilbert order, used for Mo with update (3 次元空間の点の Hilbert order への変換, Mo's algorithm (「時空間 Mo」)への応用)
3+
documentation_of: ./hilbert_order_mos_3d.hpp
4+
---
5+
6+
点 $(x, y, z)$ の 3 次元のヒルベルト曲線上の端点からの距離を計算する.
7+
8+
コンテストでの応用上重要な特徴として,座標の値が $n$ 以下の $q$ 個の点列をこの値をキーにソートする(ヒルベルトソート)ことで,パスのマンハッタン距離の総和が $O(n q^{2/3})$ で抑えられる.
9+
10+
Mo with update などの用途でクエリ処理順序の決定に利用可能で,例えば更新ありの Mo's algorithm は $O(n q^{2/3})$ で解けることになる.
11+
12+
## 使用方法
13+
14+
```cpp
15+
unsigned x, y, z;
16+
unsigned long long h = encode_hilbert_order_3d<unsigned long long>(x, y, z);
17+
```
18+
19+
### Mo with update への応用
20+
21+
長さ $n$ の列と $q$ 個のクエリが与えられて, $x, y$ が区間の両端 $l, r$ に対応し, $z$ が更新時刻 $t$ に対応するタイプの問題は,特に以下のようなコードで処理できる.なおこの手の出題ではジャッジが $O(nq)$ と $O(n q^{2/3})$ を区別する必要があるため,定数倍の実装には気をつけた方がよい.
22+
23+
#### クエリ情報・更新情報取得
24+
25+
```cpp
26+
int tick = 0;
27+
vector<tuple<unsigned long long, int, int, int, int>> queries;
28+
vector<tuple<int, int, int>> updates;
29+
30+
for (int q = 0; q < Q; ++q) {
31+
int tp, l, r, p, x;
32+
cin >> tp;
33+
if (tp == 1) {
34+
cin >> l >> r;
35+
--l;
36+
const int nq = queries.size();
37+
queries.emplace_back(encode_hilbert_order_3d<unsigned long long>(tick, l, r), l, r, tick, nq);
38+
} else {
39+
cin >> p >> x;
40+
--p;
41+
updates.emplace_back(p, A.at(p), x);
42+
A.at(p) = x;
43+
++tick;
44+
}
45+
}
46+
```
47+
48+
#### 解取得処理の実行
49+
50+
```cpp
51+
sort(queries.begin(), queries.end());
52+
53+
int l = 0, r = 0;
54+
55+
vector<int> vcnt(zs.size()); // 区間に現れる各値の計数
56+
vector<int> vhist(N + 2); // 配列 vcnt の頻度分布
57+
vhist.at(0) = vcnt.size();
58+
59+
auto add_val = [&](int v) -> void {
60+
const int cnt = vcnt[v];
61+
vcnt[v]++;
62+
vhist[cnt]--;
63+
vhist[cnt + 1]++;
64+
};
65+
66+
auto remove_val = [&](int v) -> void {
67+
--vcnt[v];
68+
const int cnt = vcnt[v];
69+
vhist[cnt]++;
70+
vhist[cnt + 1]--;
71+
};
72+
73+
vector<int> ret(queries.size(), -1);
74+
75+
for (const auto &[_, ltgt, rtgt, ticktgt, nq] : queries) {
76+
while (tick < ticktgt) {
77+
auto [p, oldval, newval] = updates[tick];
78+
A[p] = newval;
79+
if (l <= p and p < r) {
80+
remove_val(oldval);
81+
add_val(newval);
82+
}
83+
++tick;
84+
}
85+
86+
while (tick > ticktgt) {
87+
--tick;
88+
auto [p, oldval, newval] = updates[tick];
89+
A[p] = oldval;
90+
if (l <= p and p < r) {
91+
remove_val(newval);
92+
add_val(oldval);
93+
}
94+
}
95+
96+
while (ltgt < l) add_val(A[--l]);
97+
98+
while (r < rtgt) add_val(A[r++]);
99+
100+
while (l < ltgt) remove_val(A[l++]);
101+
102+
while (rtgt < r) remove_val(A[--r]);
103+
104+
/* solve subproblem
105+
ret.at(nq) = ***;
106+
*/
107+
}
108+
```
109+
110+
## 問題例
111+
112+
列の要素更新ありの Mo's algorithm が必要な問題例を以下に挙げる.
113+
114+
- [Codeforces Round 466 (Div. 2) F. Machine Learning - Codeforces](https://codeforces.com/contest/940/problem/F)
115+
- [Educational Codeforces Round 103 (Rated for Div. 2) G. Minimum Difference - Codeforces](https://codeforces.com/contest/1476/problem/G)
116+
- [Primitive Queries \| CodeChef](https://www.codechef.com/problems/DISTNUM3)
117+
- 木のパスクエリ処理・要素更新ありの Mo's algorithm で解ける.
118+
119+
## Links
120+
121+
- [An alternative sorting order for Mo's algorithm - Codeforces](https://codeforces.com/blog/entry/61203)
122+
- [Mo's algorithm - アルゴリズムとデータ構造大全](https://take44444.github.io/Algorithm-Book/range/mo/main.html)
123+
- [[2308.05673] Algorithms for Encoding and Decoding 3D Hilbert Orderings](https://arxiv.org/abs/2308.05673)

0 commit comments

Comments
 (0)