1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
| // 더 많은 정보는 42jerrykim.github.io 에서 확인하세요.
#include <bits/stdc++.h>
using namespace std;
using int64 = long long;
using i128 = __int128_t;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int m, n;
if (!(cin >> m >> n)) return 0;
vector<pair<int64,int64>> rawProd; rawProd.reserve(m); // (d, p)
for (int i = 0; i < m; ++i) {
int64 p, d; cin >> p >> d;
rawProd.emplace_back(d, p);
}
vector<pair<int64,int64>> rawCons; rawCons.reserve(n); // (e, q)
for (int j = 0; j < n; ++j) {
int64 q, e; cin >> q >> e;
rawCons.emplace_back(e, q);
}
// Producers: sort by d asc, keep minimal p per d, then keep non-dominated (p strictly decreasing as d increases)
sort(rawProd.begin(), rawProd.end()); // by d, then p
vector<pair<int64,int64>> prodByD; prodByD.reserve(rawProd.size());
for (size_t i = 0; i < rawProd.size(); ) {
int64 d = rawProd[i].first;
int64 minp = rawProd[i].second;
size_t j = i + 1;
while (j < rawProd.size() && rawProd[j].first == d) {
minp = min(minp, rawProd[j].second);
++j;
}
prodByD.emplace_back(d, minp);
i = j;
}
vector<pair<int64,int64>> P; P.reserve(prodByD.size());
for (auto &pp : prodByD) {
if (P.empty() || pp.second < P.back().second) P.push_back(pp);
}
if (P.empty()) { cout << 0 << '\n'; return 0; }
// Consumers: sort by e asc, keep maximal q per e, then keep non-dominated maxima (q strictly increasing as e increases after reverse pass)
sort(rawCons.begin(), rawCons.end()); // by e, then q
vector<pair<int64,int64>> consByE; consByE.reserve(rawCons.size());
for (size_t i = 0; i < rawCons.size(); ) {
int64 e = rawCons[i].first;
int64 maxq = rawCons[i].second;
size_t j = i + 1;
while (j < rawCons.size() && rawCons[j].first == e) {
maxq = max(maxq, rawCons[j].second);
++j;
}
consByE.emplace_back(e, maxq);
i = j;
}
// Build consumers frontier with q strictly increasing as e increases.
// On ties of q, keep the later (larger e) point.
vector<pair<int64,int64>> C; C.reserve(consByE.size());
for (const auto &eq : consByE) {
if (C.empty() || eq.second > C.back().second) {
C.push_back(eq);
} else if (eq.second == C.back().second) {
C.back().first = eq.first; // keep larger e for the same q
}
}
if (C.empty()) { cout << 0 << '\n'; return 0; }
const int I = (int)P.size();
const int J = (int)C.size();
vector<int64> D(I), PP(I), E(J), Q(J);
for (int i = 0; i < I; ++i) { D[i] = P[i].first; PP[i] = P[i].second; }
for (int j = 0; j < J; ++j) { E[j] = C[j].first; Q[j] = C[j].second; }
auto solve = [&](auto&& self, int il, int ir, int jl, int jr, int64 &answer) -> void {
if (il > ir || jl > jr) return;
int im = (il + ir) >> 1;
i128 bestScore = (i128)LLONG_MIN;
int bestJ = jl;
for (int j = jl; j <= jr; ++j) {
// For divide-and-conquer splitting (Monge), use raw score (can be negative)
i128 sc = (i128)(E[j] - D[im]) * (i128)(Q[j] - PP[im]);
if (sc > bestScore) {
bestScore = sc;
bestJ = j;
}
// For actual profit, only count valid contracts: e > d and q > p
if (E[j] > D[im] && Q[j] > PP[im]) {
i128 prof = (i128)(E[j] - D[im]) * (i128)(Q[j] - PP[im]);
if (prof > (i128)answer) answer = (int64)prof;
}
}
self(self, il, im - 1, jl, bestJ, answer);
self(self, im + 1, ir, bestJ, jr, answer);
};
int64 ans = 0;
solve(solve, 0, I - 1, 0, J - 1, ans);
cout << ans << '\n';
return 0;
}
|