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
| // 더 많은 정보는 42jerrykim.github.io 에서 확인하세요.
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
static const ll X_MIN = -1000000000LL;
static const ll X_MAX = 1000000000LL + 1; // [X_MIN, X_MAX)
static const long long NEG_INF = (long long)-4e18;
struct Line { ll m, b; }; // y = m x + b
struct LiChao {
struct Node {
Line ln; int lc, rc; // child indices
Node() : ln({0, NEG_INF}), lc(0), rc(0) {}
};
vector<Node> pool; int root = 0;
vector<pair<int, Line>> history; // (index, old line)
LiChao() { pool.reserve(1<<22); pool.push_back(Node()); }
int new_node(){ pool.push_back(Node()); return (int)pool.size()-1; }
inline __int128 f(const Line& L, ll x) const { return (__int128)L.m * x + L.b; }
void add_line(Line nw){ add_line(root, X_MIN, X_MAX, nw); }
void add_line(int &n, ll l, ll r, Line nw){
if(n==0) n = new_node();
int idx = n; ll mid = (l + r) >> 1;
Line lo = pool[idx].ln, hi = nw;
bool leftBetter = f(hi, l) > f(lo, l);
bool midBetter = f(hi, mid) > f(lo, mid);
if(midBetter) swap(lo, hi);
if(pool[idx].ln.m != lo.m || pool[idx].ln.b != lo.b){
history.emplace_back(idx, pool[idx].ln);
pool[idx].ln = lo;
}
if(r - l == 1) return;
if(leftBetter != midBetter) add_line(pool[idx].lc, l, mid, hi);
else add_line(pool[idx].rc, mid, r, hi);
}
ll query(ll x) const { return root? (ll)query_rec(root, X_MIN, X_MAX, x) : NEG_INF; }
__int128 query_rec(int n, ll l, ll r, ll x) const{
if(n==0) return (__int128)NEG_INF;
const Node &nd = pool[n]; __int128 res = f(nd.ln, x);
if(r - l == 1) return res; ll mid = (l + r) >> 1;
if(x < mid) return max(res, query_rec(nd.lc, l, mid, x));
return max(res, query_rec(nd.rc, mid, r, x));
}
void rollback(size_t checkpoint){
while(history.size() > checkpoint){ auto [idx, oldLine] = history.back(); history.pop_back(); pool[idx].ln = oldLine; }
}
};
struct SegmentTree {
int n; vector<vector<Line>> bucket; // 1..n+1 구간
SegmentTree(int n_) : n(n_), bucket(4*(n_+5)) {}
void add_range(int node, int L, int R, int ql, int qr, const Line &ln){
if(qr<=L || R<=ql) return; if(ql<=L && R<=qr){ bucket[node].push_back(ln); return; }
int mid=(L+R)>>1; add_range(node<<1, L, mid, ql, qr, ln); add_range(node<<1|1, mid, R, ql, qr, ln);
}
};
int main(){
ios::sync_with_stdio(false); cin.tie(nullptr);
int n; if(!(cin>>n)) return 0;
vector<int> type(n+1); vector<long long> A(n+1), B(n+1), X(n+1);
vector<int> start(n+1, -1), fin(n+1, -1);
vector<vector<pair<long long,int>>> queriesAt(n+2);
int qcnt=0;
for(int i=1;i<=n;++i){
cin>>type[i];
if(type[i]==1){ cin>>A[i]>>B[i]; start[i]=i; fin[i]=n+1; }
else if(type[i]==2){ int idx; cin>>idx; fin[idx]=i; }
else { cin>>X[i]; queriesAt[i].push_back({X[i], qcnt++}); }
}
SegmentTree seg(n+1);
for(int i=1;i<=n;++i){ if(type[i]==1){ int L=start[i], R=fin[i]; if(L<R) seg.add_range(1,1,n+1,L,R, Line{A[i],B[i]}); } }
vector<string> ans(qcnt); LiChao lichao; int active=0;
function<void(int,int,int)> dfs = [&](int node,int L,int R){
size_t cp = lichao.history.size(); int saved=active;
for(const auto &ln: seg.bucket[node]){ lichao.add_line(ln); ++active; }
if(L+1==R){
for(auto &q: queriesAt[L]){ if(active==0) ans[q.second] = "EMPTY"; else ans[q.second] = to_string(lichao.query(q.first)); }
} else {
int mid=(L+R)>>1; dfs(node<<1,L,mid); dfs(node<<1|1,mid,R);
}
lichao.rollback(cp); active=saved;
};
dfs(1,1,n+1);
for(int i=0;i<qcnt;++i) cout<<ans[i]<<'\n';
return 0;
}
|