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

Skip to content

Commit 9fb2c86

Browse files
authored
Merge pull request #400 from hitonanode/enhance-smawk
enhance SMAWK / add monotone minima
2 parents b375739 + 000d6c6 commit 9fb2c86

File tree

5 files changed

+169
-8
lines changed

5 files changed

+169
-8
lines changed
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
#pragma once
2+
#include <cassert>
3+
#include <functional>
4+
#include <vector>
5+
6+
// finding minimum element for each row of N*M matrix
7+
// Constraints: the solution is monotonically non-decreasing
8+
// Complexity: O(NM logM)
9+
// Reference:
10+
// https://topcoder-g-hatena-ne-jp.jag-icpc.org/spaghetti_source/20120923/1348327542.html
11+
// Verify: https://mofecoder.com/contests/monoxercon_202508/tasks/monoxercon_202508_k
12+
template <class T>
13+
std::vector<std::pair<int, T>>
14+
MonotoneMinima(int N, int M, const std::function<T(int i, int j)> &weight) {
15+
std::vector<std::pair<int, T>> minima(N);
16+
17+
auto rec = [&](auto &&self, int il, int ir, int jl, int jr) -> void {
18+
if (il >= ir or jl >= jr) return;
19+
const int im = (il + ir) / 2;
20+
T w = weight(im, jl);
21+
int j_argmin = jl;
22+
for (int j = jl + 1; j < jr; ++j) {
23+
if (T wt = weight(im, j); wt < w) w = wt, j_argmin = j;
24+
}
25+
minima[im] = {j_argmin, w};
26+
self(self, il, im, jl, j_argmin + 1);
27+
self(self, im + 1, ir, j_argmin, jr);
28+
};
29+
rec(rec, 0, N, 0, M);
30+
31+
return minima;
32+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
---
2+
title: Monotone minima (Monotone な行列の行最小値の効率的な探索)
3+
documentation_of: ./monotone_minima.hpp
4+
---
5+
6+
`SMAWK()` 関数は,monotone な $N \times M$ 行列,すなわち各行の最小値の位置が行を下るにつれ右に移動していくような行列について,各行の最小値の位置およびその値を $O((N + M) \log (N + M))$ で取得する.
7+
8+
[SMAWK](./smawk.md) のライブラリと互換性があり,SMAWK が使用されている箇所は本関数で代替可能(最悪計算量のオーダーはこちらの方に log がつくが,問題によってはこちらの方が実測上速い).
9+
10+
## 使用方法
11+
12+
例えば辺重みが Monge な $N$ 頂点の DAG で頂点 $0$ から各頂点への最短路重みを求めたいとき, $N$ 行 $N - 1$ 列の行列を $(j, i)$ 成分が「直前に頂点 $i$ を経由し頂点 $j$ に到達する場合の最短路重み」であるようなものとして定めると本関数が適用できる(SMAWK も適用できるが).
13+
14+
```cpp
15+
using T = long long;
16+
T inf = 1LL << 60;
17+
int N;
18+
vector<T> dp(N, inf);
19+
dp[0] = 0;
20+
21+
auto weight = [&](int s, int t) -> T { /* Implement */ };
22+
23+
const auto res = MonotoneMinima<T>(
24+
N, N - 1, [&](int j, int i) -> T { return i < j ? dp[i] + weight(i, j) : inf; });
25+
```
26+
27+
## 問題例
28+
29+
- [K. Coupon - Monoxer Programming Contest for Engineers](https://mofecoder.com/contests/monoxercon_202508/tasks/monoxercon_202508_k)
30+
31+
## Links
32+
33+
- [Totally Monotone Matrix Searching (SMAWK algorithm) - 週刊 spaghetti_source - TopCoder部](https://topcoder-g-hatena-ne-jp.jag-icpc.org/spaghetti_source/20120923/1348327542.html)

other_algorithms/smawk.hpp

Lines changed: 72 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
#include <vector>
77

88
// SMAWK: finding minima of totally monotone function f(i, j) (0 <= i < N, 0 <= j < M) for each i
9-
// Constraints: every submatrix of f(i, j) is monotone.
9+
// Constraints: every submatrix of f(i, j) is monotone (= totally monotone).
1010
// Complexity: O(N + M)
1111
// Reference:
1212
// https://topcoder-g-hatena-ne-jp.jag-icpc.org/spaghetti_source/20120923/1348327542.html
@@ -46,8 +46,19 @@ std::vector<std::pair<int, T>> SMAWK(int N, int M, const std::function<T(int i,
4646
return minima;
4747
}
4848

49+
// Find minima of totally ANTI-monotone function f(i, j) (0 <= i < N, 0 <= j < M) for each i
50+
// Constraints: every submatrix of f(i, j) is anti-monotone.
51+
// Complexity: O(N + M)
52+
template <class T>
53+
std::vector<std::pair<int, T>>
54+
SMAWKAntiMonotone(int N, int M, const std::function<T(int i, int j)> &weight) {
55+
auto minima = SMAWK<T>(N, M, [&](int i, int j) -> T { return weight(i, M - 1 - j); });
56+
for (auto &p : minima) p.first = M - 1 - p.first;
57+
return minima;
58+
}
59+
4960
// Concave max-plus convolution
50-
// b must be concave
61+
// **b MUST BE CONCAVE**
5162
// Complexity: O(n + m)
5263
// Verify: https://www.codechef.com/problems/MAXPREFFLIP
5364
template <class S, S INF>
@@ -60,10 +71,7 @@ std::vector<S> concave_max_plus_convolution(const std::vector<S> &a, const std::
6071
}
6172
return true;
6273
};
63-
64-
bool a_concave = is_concave(a), b_concave = is_concave(b);
65-
assert(a_concave or b_concave);
66-
if (!b_concave) return concave_max_plus_convolution<S, INF>(b, a);
74+
assert(is_concave(b));
6775

6876
auto select = [&](int i, int j) -> S {
6977
int aidx = j, bidx = i - j;
@@ -75,3 +83,61 @@ std::vector<S> concave_max_plus_convolution(const std::vector<S> &a, const std::
7583
for (auto x : minima) ret.push_back(-x.second);
7684
return ret;
7785
}
86+
87+
// Concave min-plus convolution
88+
// **b MUST BE CONCAVE**
89+
// Complexity: O((n + m)log(n + m))
90+
template <class S>
91+
std::vector<S> concave_min_plus_convolution(const std::vector<S> &a, const std::vector<S> &b) {
92+
const int n = a.size(), m = b.size();
93+
94+
auto is_concave = [&](const std::vector<S> &u) -> bool {
95+
for (int i = 1; i + 1 < int(u.size()); ++i) {
96+
if (u[i - 1] + u[i + 1] > u[i] + u[i]) return false;
97+
}
98+
return true;
99+
};
100+
assert(is_concave(b));
101+
102+
std::vector<S> ret(n + m - 1);
103+
std::vector<int> argmin(n + m - 1, -1);
104+
105+
// mat[i][j] = a[j] + b[i - j]
106+
auto is_valid = [&](int i, int j) { return 0 <= i - j and i - j < m; };
107+
auto has_valid = [&](int il, int ir, int jl, int jr) {
108+
if (il >= ir or jl >= jr) return false;
109+
return is_valid(il, jl) or is_valid(il, jr - 1) or is_valid(ir - 1, jl) or
110+
is_valid(ir - 1, jr - 1);
111+
};
112+
113+
auto rec = [&](auto &&self, int il, int ir, int jl, int jr) -> void {
114+
if (!has_valid(il, ir, jl, jr)) return;
115+
116+
if (is_valid(il, jr - 1) and is_valid(ir - 1, jl)) {
117+
auto select = [&](int i, int j) -> S { return a[j + jl] + b[(i + il) - (j + jl)]; };
118+
const auto res = SMAWKAntiMonotone<S>(ir - il, jr - jl, select);
119+
for (int idx = 0; idx < ir - il; ++idx) {
120+
const int i = il + idx;
121+
if (argmin[i] == -1 or res[idx].second < ret[i]) {
122+
ret[i] = res[idx].second;
123+
argmin[i] = res[idx].first + jl;
124+
}
125+
}
126+
} else {
127+
if (const int di = ir - il, dj = jr - jl; di > dj) {
128+
const int im = (il + ir) / 2;
129+
self(self, il, im, jl, jr);
130+
self(self, im, ir, jl, jr);
131+
} else {
132+
const int jm = (jl + jr) / 2;
133+
self(self, il, ir, jl, jm);
134+
self(self, il, ir, jm, jr);
135+
}
136+
}
137+
};
138+
139+
rec(rec, 0, n + m - 1, 0, n);
140+
141+
return ret;
142+
// return argmin; // If you want argmin (0 <= argmin[idx] < len(a))
143+
}

other_algorithms/smawk.md

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
---
2-
title: Totally Monotone Matrix Searching (SMAWK)
2+
title: Totally Monotone Matrix Searching (SMAWK), concave max-plus / min-plus convolution
33
documentation_of: ./smawk.hpp
44
---
55

6-
Totally monotone な $N \times M$ 行列について,各行の最小値の位置を $O(N + M)$ で取得する.
6+
`SMAWK()` 関数は,totally monotone な $N \times M$ 行列について,各行の最小値の位置およびその値を $O(N + M)$ で取得する.
7+
また, totally anti-monotone (不等式の向きが逆,つまり行最小値の位置が行に対して単調非増加などの性質を持つ)な行列に対して同様に最小値を取得する `SMAWKAntiMonotone()` 関数も実装されている.
78

89
## 使用方法
910

@@ -33,10 +34,18 @@ vector<int> a, b;
3334
vector<int> c = concave_max_plus_convolution<int, 1 << 30>(a, b);
3435
```
3536

37+
### 応用例:concave min-plus convolution
38+
39+
上記と同様の状況設定で,逆に min-plus convolution も SMAWK を応用することで $O((n + m) \log (n + m))$ で計算できる.
40+
41+
このとき,SMAWK を適用したい仮想的な $(n + m - 1) \times n$ 行列は,無効値の位置の都合が悪く totally monotone でも totally anti-monotone でもないため直接扱えない.ここで,有効値が入った平行四辺形の領域をうまく矩形に分割していくことで `SMAWKAntiMonotone()` 関数が適用可能な状況にしている(この分割統治で計算量に log がつく).
42+
3643
## 問題例
3744

3845
- [Communication Channel \| CodeChef](https://www.codechef.com/problems/COMMCHA)
3946
- [Maximal Prefix After Flip \| CodeChef](https://www.codechef.com/problems/MAXPREFFLIP)
47+
- [Min Plus Convolution (Convex and Arbitrary)](https://judge.yosupo.jp/problem/min_plus_convolution_convex_arbitrary)
48+
- [Min Plus Convolution (Concave and Arbitrary)](https://judge.yosupo.jp/problem/min_plus_convolution_concave_arbitrary)
4049

4150
## Links
4251

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
#define PROBLEM "https://judge.yosupo.jp/problem/min_plus_convolution_concave_arbitrary"
2+
3+
#include "../smawk.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+
12+
int N, M;
13+
cin >> N >> M;
14+
vector<int> A(N), B(M);
15+
for (auto &a : A) cin >> a;
16+
for (auto &b : B) cin >> b;
17+
18+
vector<int> ret = concave_min_plus_convolution<int>(B, A);
19+
20+
for (int i = 0; i < N + M - 1; ++i) cout << ret[i] << " \n"[i + 1 == N + M - 1];
21+
}

0 commit comments

Comments
 (0)