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
5364template <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+ }
0 commit comments