Submission #217162

#TimeUsernameProblemLanguageResultExecution timeMemory
217162rama_pangSweeping (JOI20_sweeping)C++14
100 / 100
8119 ms787204 KiB
#include <bits/stdc++.h> using namespace std; class Sweeping { private: struct Point { int x, y; Point() {} Point(int x, int y) : x(x), y(y) {} }; struct Operation { int L; // If the operation is of type H, L denotes (N - L[i]) for operation i. L is actually the x-coordinate of the bottomright corner of the triangle formed by the operation, not its length. bool vertical; Operation() {} Operation(int L, bool v) : L(L), vertical(v) {} bool IsVertical() { return vertical; } const bool IsVertical() const { return vertical; } bool operator < (const Operation &o) const { return make_pair(L, vertical) < make_pair(o.L, o.vertical); } }; class SegmentTree { // Range Minimum Query private: int sz; vector<int> value; vector<int> tree; int pull(int a, int b) { if (a == -1) return b; if (b == -1) return a; return value[a] < value[b] ? a : b; } public: void Build(const vector<int> &val, const vector<int> &base) { sz = val.size(); value = val; tree.resize(2 * sz); for (int i = 0; i < sz; i++) { tree[i + sz] = base[i]; } for (int i = sz - 1; i >= 0; i--) { tree[i] = pull(tree[i * 2], tree[i * 2 + 1]); } } int Query(int l, int r) { int res = -1; for (l += sz, r += sz + 1; l < r; l /= 2, r /= 2) { if (l & 1) res = pull(res, tree[l++]); if (r & 1) res = pull(res, tree[--r]); } return res; } }; class Transformation { // Given a set of operations Q, find out where point (x, y) will be after all operations private: int sz = 0; int N; bool isBuilt = false; vector<Operation> ops; // operations in chronological order vector<Operation> ops_sorted_L; // sorted by Operation.L SegmentTree rmq; void Unique() { // delete multiple elements (since they don't have any effect anyways and make implementation harder) vector<Operation> new_ops; set<Operation> unique_ops; for (int i = 0; i < sz; i++) { if (unique_ops.count(ops[i]) == 1) { continue; } new_ops.emplace_back(ops[i]); unique_ops.emplace(ops[i]); } ops = new_ops; sz = ops.size(); } void Build() { Unique(); vector<vector<int>> adj(sz); function<void(int, vector<int>&)> dfs = [&](int u, vector<int> &postorder) { for (auto &v : adj[u]) dfs(v, postorder); // to compute the minimum value easily. See further for details. postorder.emplace_back(u); }; vector<int> roots; set<pair<Operation, int>> triangles; // (Operation, time in chronological order) for (int i = 0; i < sz; i++) { auto it = triangles.lower_bound(make_pair(ops[i], i)); bool isRoot = true; if (it != end(triangles)) { if (ops[i].IsVertical() && (*it).first.IsVertical()) { // if there is an operation H that results in same coordinate, we do not consider it since // it forms a dot as their triangle, and there is no connection between that dot and curent // operation, Incidentally, sorting means that an operation H will be encountered in that // very case, so we do not need to worry about it. adj[(*it).second].emplace_back(i); // swapping i and it will make no difference - take the one with higher finak y-coordinate. isRoot = false; } } if (it != begin(triangles)) { it--; if (!ops[i].IsVertical() && !(*it).first.IsVertical()) { // if there is an operation V that results in same coordinate, we do not consider it since // it forms a dot as their triangle, and there is no connection between that dot and curent // operation. Incidentally, sorting means that an operation V will be encountered in that // very case, so we do not need to worry about it. adj[(*it).second].emplace_back(i); isRoot = false; } } if (isRoot) { roots.emplace_back(i); } triangles.emplace(ops[i], i); } vector<int> new_chronological_order; for (auto &r : roots) { dfs(r, new_chronological_order); // postorder dfs } vector<int> when(sz), base; for (int i = 0; i < sz; i++) { when[new_chronological_order[i]] = i; // compute time values for every indices } for (auto &t : triangles) { ops_sorted_L.emplace_back(t.first); // operation sorted by L base.emplace_back(t.second); // indices } rmq.Build(when, base); return; } Point Transform_(Point p) { int l = lower_bound(begin(ops_sorted_L), end(ops_sorted_L), Operation(p.x, false)) - begin(ops_sorted_L); int r = upper_bound(begin(ops_sorted_L), end(ops_sorted_L), Operation(N - p.y, true)) - begin(ops_sorted_L) - 1; if (l > r) return p; // [l, r] are regions p can potentially end up in int mn = rmq.Query(l, r); if (ops[mn].IsVertical()) { if (p.x <= ops[mn].L) p.y = max(p.y, N - ops[mn].L); } else { if (p.y <= N - ops[mn].L) p.x = max(p.x, ops[mn].L); // ops[mn].L = N - L[i], so L[i] = N - pos[mn].L } // update [l, r] after being swept by operation mn int mn_loc = lower_bound(begin(ops_sorted_L), end(ops_sorted_L), ops[mn]) - begin(ops_sorted_L); // where mn is in ops_sorted_L if (ops[mn].IsVertical()) { r = mn_loc - 1; // first is operation V, then operation H } else { l = mn_loc + 1; // first is operation H, then operation V } if (l > r) return p; // there is one more operation (if previous is H, then now it's V; vice versa) mn = rmq.Query(l, r); if (ops[mn].IsVertical()) { if (p.x <= ops[mn].L) p.y = max(p.y, N - ops[mn].L); } else { if (p.y <= N - ops[mn].L) p.x = max(p.x, ops[mn].L); // ops[mn].L = N - L[i], so L[i] = N - ops[mn].L } return p; } public: Transformation() {} Transformation(int N) : N(N) {} void AddOperation(const Operation &o) { ops.emplace_back(o); sz++; } Point Transform(Point p) { if (!isBuilt) isBuilt = true, Build(); return Transform_(p); } }; class QueryTree { private: int sz; vector<Transformation> tree; void AddOperation(int n, int tl, int tr, int pos, const Operation &op) { tree[n].AddOperation(op); if (tl == tr) return; int mid = (tl + tr) / 2; if (pos <= mid) { AddOperation(n * 2, tl, mid, pos, op); } else { AddOperation(n * 2 + 1, mid + 1, tr, pos, op); } } void Query(int n, int tl, int tr, int l, int r, Point &p) { if (tr < l || r < tl || tl > tr || l > r) return; if (l <= tl && tr <= r) return void(p = tree[n].Transform(p)); int mid = (tl + tr) / 2; Query(n * 2, tl, mid, l, r, p); Query(n * 2 + 1, mid + 1, tr, l, r, p); } public: QueryTree(int N, int Q) { sz = Q; tree.assign(4 * sz, Transformation(N)); } void AddOperation(int pos, Operation op) { AddOperation(1, 0, sz - 1, pos, op); } Point Query(int l, int r, Point p) { Query(1, 0, sz - 1, l, r, p); return p; } }; public: void Solve() { ios::sync_with_stdio(0); cin.tie(0), cout.tie(0); int N, M, Q; vector<pair<Point, int>> P; // (Point, time_added) cin >> N >> M >> Q; QueryTree QTree(N, Q); for (int i = 0; i < M; i++) { int x, y; cin >> x >> y; P.emplace_back(Point(x, y), 0); } for (int i = 0, op_cnt = 0; i < Q; i++) { int T; cin >> T; if (T == 1) { int pos; cin >> pos; pos--; Point res = QTree.Query(P[pos].second, op_cnt - 1, P[pos].first); cout << res.x << " " << res.y << "\n"; } else if (T == 2 || T == 3) { int L; cin >> L; if (T == 2) QTree.AddOperation(op_cnt, Operation(N - L, false)); // operation H if (T == 3) QTree.AddOperation(op_cnt, Operation(L, true)); // operation V op_cnt++; } else if (T == 4) { int x, y; cin >> x >> y; P.emplace_back(Point(x, y), op_cnt); } } } }; int main() { Sweeping Solver; Solver.Solve(); return 0; }
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...