UOJ #191. 【集训队互测2016】Unknown 题解
Description
有一个元素为向量的序列,下标从
- 在
的末尾添加一个元素 。 - 删除
的末尾元素。 - 询问下标在
区间内的元素中, 的最大值。
其中
任意时刻
1 操作个数不超过
3 操作个数不超过
Solution
首先如果没有删除操作,就直接用向量集这题的做法对于线段树上的节点维护凸包,当一个节点被插满后再建凸包。
加上删除操作后如果暴力重构显然会超时,考虑怎么让这题的势能变得正确。
注意到对于二进制分组后每层的最后一个插满的节点,如果查询时不考虑这个节点,直接向下递归的话也能保证查询复杂度的正确。
所以对于每次插入操作就建好每层除了最后一个插满的节点的凸包,对于删除操作如果删到一个之前已经构建好凸包的节点就把这个节点的凸包清空,这样就能保证重构的总点数是
时间复杂度:
Code
#include <bits/stdc++.h> // #define int int64_t using i64 = int64_t; using pii = std::pair<i64, i64>; const int kMaxN = 5e5 + 5, kMod = 998244353; int _tp, n, q; pii pr[kMaxN]; std::vector<int> vec[(1 << 19) + 5], v[(1 << 19) + 5]; bool vis[(1 << 19) + 5]; /* v[x][0/1] : 线段树 x 号节点的上/下凸包 */ int fix(i64 x) { return (x % kMod + kMod) % kMod; } pii sub(pii a, pii b) { return {a.first - b.first, a.second - b.second}; } i64 mul(pii a, pii b) { return a.first * b.second - a.second * b.first; } i64 func(pii a, pii b) { return a.first * b.second + a.second * b.first; } int getid(int dep, int x) { return (1 << (18 - dep)) + x; } void build(int x) { static std::vector<int> tvec; vis[x] = 1; tvec = vec[x]; std::sort(tvec.begin(), tvec.end(), [&] (int i, int j) { return pr[i] < pr[j]; }); v[x].clear(), v[x].shrink_to_fit(); for (auto i : tvec) { for (; v[x].size() >= 2 && mul(sub(pr[i], pr[v[x].back()]), sub(pr[v[x].back()], pr[v[x][v[x].size() - 2]])) <= 0; v[x].pop_back()) {} v[x].emplace_back(i); } } void clear(int x) { vis[x] = 0, v[x].clear(); } i64 query(int x, pii p) { int L = 0, R = v[x].size(), res = 0; while (L + 1 < R) { int mid = (L + R) >> 1; if (func(p, pr[v[x][mid]]) >= func(p, pr[v[x][mid - 1]])) L = res = mid; else R = mid; } return func(p, pr[v[x][res]]); } i64 query(int x, int l, int r, int ql, int qr, pii p) { if (l > qr || r < ql) return -1e18; else if (l >= ql && r <= qr) { if (l == r) return func(p, pr[l]); else if (vis[x]) return query(x, p); } int mid = (l + r) >> 1; return std::max(query(x << 1, l, mid, ql, qr, p), query(x << 1 | 1, mid + 1, r, ql, qr, p)); } void dickdreamer() { int ans = 0; n = 0; for (int cs = 1; cs <= q; ++cs) { int op; pii p; std::cin >> op; if (op == 1) { std::cin >> p.first >> p.second; p.first *= -1; pr[n] = p; for (int i = 0; i <= 18; ++i) { int t = (n >> i); vec[getid(i, t)].emplace_back(n); if ((n % (1 << i)) == (1 << i) - 1 && t && !vis[getid(i, t - 1)]) { build(getid(i, t - 1)); } } ++n; } else if (op == 2) { --n; for (int i = 0; i <= 18; ++i) { int t = (n >> i); vec[getid(i, t)].pop_back(); if ((n % (1 << i)) == (1 << i) - 1 && vis[getid(i, t)]) { clear(getid(i, t)); } } } else { int l, r; std::cin >> l >> r >> p.first >> p.second; ans ^= fix(query(1, 0, (1 << 18) - 1, l - 1, r - 1, p)); } } std::cout << ans << '\n'; for (int i = 0; i < (1 << 19); ++i) { vec[i].clear(), vec[i].shrink_to_fit(); v[i].clear(), v[i].shrink_to_fit(); vis[i] = 0; } } int32_t main() { #ifdef ORZXKR freopen("in.txt", "r", stdin); freopen("out.txt", "w", stdout); #endif std::ios::sync_with_stdio(0), std::cin.tie(0), std::cout.tie(0); int T = 1; // std::cin >> T; std::cin >> _tp; while (std::cin >> q && q) dickdreamer(); // std::cerr << 1.0 * clock() / CLOCKS_PER_SEC << "s\n"; return 0; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步