Skip to content

Commit 9324305

Browse files
authored
multidim-index (#325)
1 parent bc8d722 commit 9324305

File tree

3 files changed

+170
-0
lines changed

3 files changed

+170
-0
lines changed

utilities/multidim_index.hpp

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
#include <cassert>
2+
#include <vector>
3+
4+
// n-dimentional index <-> 1-dimentional index converter
5+
// [a_0, ..., a_{dim - 1}] <-> a_0 + a_1 * size_0 + ... + a_{dim - 1} * (size_0 * ... * size_{dim - 2})
6+
template <class T = int> struct multidim_index {
7+
int dim = 0;
8+
T _size = 1;
9+
std::vector<T> sizes;
10+
std::vector<T> weights;
11+
12+
multidim_index() = default;
13+
14+
explicit multidim_index(const std::vector<T> &sizes)
15+
: dim(sizes.size()), sizes(sizes), weights(dim, T(1)) {
16+
for (int d = 0; d < (int)sizes.size(); ++d) {
17+
assert(sizes.at(d) > 0);
18+
_size *= sizes.at(d);
19+
if (d >= 1) weights.at(d) = weights.at(d - 1) * sizes.at(d - 1);
20+
}
21+
}
22+
23+
T size() const { return _size; }
24+
25+
T flat_index(const std::vector<T> &encoded_vec) const {
26+
assert((int)encoded_vec.size() == (int)sizes.size());
27+
T ret = 0;
28+
for (int d = 0; d < (int)sizes.size(); ++d) {
29+
assert(0 <= encoded_vec.at(d) and encoded_vec.at(d) < sizes.at(d));
30+
ret += encoded_vec.at(d) * weights.at(d);
31+
}
32+
return ret;
33+
}
34+
35+
std::vector<T> encode(T flat_index) const {
36+
assert(0 <= flat_index and flat_index < size());
37+
std::vector<T> ret(sizes.size());
38+
for (int d = (int)sizes.size() - 1; d >= 0; --d) {
39+
ret.at(d) = flat_index / weights.at(d);
40+
flat_index %= weights.at(d);
41+
}
42+
return ret;
43+
}
44+
45+
template <class F> void lo_to_hi(F f) {
46+
for (int d = 0; d < (int)sizes.size(); ++d) {
47+
if (sizes.at(d) == 1) continue;
48+
49+
T i = 0;
50+
std::vector<T> ivec(sizes.size());
51+
52+
int cur = sizes.size();
53+
54+
while (true) {
55+
f(i, i + weights.at(d));
56+
--cur;
57+
58+
while (cur >= 0 and ivec.at(cur) + 1 == sizes.at(cur) - (cur == d)) {
59+
i -= ivec.at(cur) * weights.at(cur);
60+
ivec.at(cur--) = 0;
61+
}
62+
63+
if (cur < 0) break;
64+
65+
++ivec.at(cur);
66+
i += weights.at(cur);
67+
cur = sizes.size();
68+
}
69+
}
70+
}
71+
72+
// Subset sum (fast zeta transform)
73+
template <class U> void subset_sum(std::vector<U> &vec) {
74+
assert((T)vec.size() == size());
75+
lo_to_hi([&](T lo, T hi) { vec.at(hi) += vec.at(lo); });
76+
}
77+
78+
// Inverse of subset sum (fast moebius transform)
79+
template <class U> void subset_sum_inv(std::vector<U> &vec) {
80+
assert((T)vec.size() == size());
81+
const T s = size() - 1;
82+
lo_to_hi([&](T dummylo, T dummyhi) { vec.at(s - dummylo) -= vec.at(s - dummyhi); });
83+
}
84+
85+
// Superset sum (fast zeta transform)
86+
template <class U> void superset_sum(std::vector<U> &vec) {
87+
assert((T)vec.size() == size());
88+
const T s = size() - 1;
89+
lo_to_hi([&](T dummylo, T dummyhi) { vec.at(s - dummyhi) += vec.at(s - dummylo); });
90+
}
91+
92+
// Inverse of superset sum (fast moebius transform)
93+
template <class U> void superset_sum_inv(std::vector<U> &vec) {
94+
assert((T)vec.size() == size());
95+
lo_to_hi([&](T lo, T hi) { vec.at(lo) -= vec.at(hi); });
96+
}
97+
};

utilities/multidim_index.md

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
---
2+
title: Multidimensional index
3+
documentation_of: ./multidim_index.hpp
4+
---
5+
6+
多次元の添え字を 1 次元に潰す処理とその逆変換の実装.
7+
8+
## 使用方法
9+
10+
### 添字変換
11+
12+
以下のような C++ コードを実行すると,以下のような結果を得る.
13+
14+
```cpp
15+
multidim_index mi({2, 3, 4});
16+
17+
for (int i = 0; i < mi.size(); ++i) {
18+
const auto vec = mi.encode(i);
19+
assert(mi.flat_index(vec) == i);
20+
21+
cout << i << ": (";
22+
for (int x : vec) cout << x << ",";
23+
cout << ")\n";
24+
}
25+
```
26+
27+
```txt
28+
0: (0,0,0,)
29+
1: (1,0,0,)
30+
2: (0,1,0,)
31+
3: (1,1,0,)
32+
4: (0,2,0,)
33+
5: (1,2,0,)
34+
6: (0,0,1,)
35+
...
36+
23: (1,2,3,)
37+
```
38+
39+
### 累積和やその逆変換
40+
41+
通常の `+=` 演算子による累積和処理に関しては, `subset_sum()` / `subset_sum_inv()` (下側累積和およびその逆変換)・ `superset_sum()` / `superset_sum_inv()` (上側累積和およびその逆変換)が提供されている.
42+
43+
より一般の演算を行いたい場合は,ラムダ式 `f` を用意した上で `lo_to_hi(F f)` 関数を使えばよい.
44+
45+
## 問題例
46+
47+
- [AtCoder Beginner Contest 335(Sponsored by Mynavi) G - Discrete Logarithm Problems](https://atcoder.jp/contests/abc335/tasks/abc335_g)
48+
- $P - 1$ の正の約数全てをキーとする DP テーブルで,整序関係をもとに累積和を取る. [参考提出](https://atcoder.jp/contests/abc335/submissions/49118789)
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
#define PROBLEM "https://judge.yosupo.jp/problem/bitwise_and_convolution"
2+
#include "../../modint.hpp"
3+
#include "../multidim_index.hpp"
4+
5+
#include <iostream>
6+
#include <vector>
7+
using namespace std;
8+
9+
int main() {
10+
cin.tie(nullptr), ios::sync_with_stdio(false);
11+
int N;
12+
cin >> N;
13+
14+
multidim_index mi(std::vector<int>(N, 2));
15+
vector<ModInt<998244353>> A(1 << N), B(1 << N);
16+
17+
for (auto &x : A) cin >> x;
18+
for (auto &x : B) cin >> x;
19+
mi.superset_sum(A);
20+
mi.superset_sum(B);
21+
for (int i = 0; i < 1 << N; ++i) A.at(i) *= B.at(i);
22+
mi.superset_sum_inv(A);
23+
24+
for (auto x : A) cout << x << ' ';
25+
}

0 commit comments

Comments
 (0)