傳送門
這個題目實際上能夠創建出樹,而後重鏈剖分維護一條鏈的凸包
而後離線詢問排序斜率作到 \(nlog^2n\),或者點分治+平衡樹也行
可是這個題目卡空間,數組一不當心就爆了卡一卡也能過
考慮其它空間常數小而且又好寫的作法
根據通常的二進制分組的方法,每次這個塊滿了就合併兒子的凸包
這樣顯然不對,只要又刪又加就假了
咱們換一種方法,每次這個塊滿了就合併線段樹同一層前一個節點的兒子的凸包
這樣每次都要花費 \(len\) 次添加操做才能換來一次 \(2len\) 的合併
此時的沒有合併節點頂多 \(2log\) 個
查詢就定位每一個節點,在凸包上二分便可
時間 \(nlog^2n\) 空間 \(nlogn\)c++
# include <bits/stdc++.h> using namespace std; typedef long long ll; namespace IO { const int maxn(1 << 21 | 1); char ibuf[maxn], *iS, *iT, c; int f; inline char Getc() { return iS == iT ? (iT = (iS = ibuf) + fread(ibuf, 1, maxn, stdin), (iS == iT ? EOF : *iS++)) : *iS++; } template <class Int> inline void In(Int &x) { for (c = Getc(), f = 1; c < '0' || c > '9'; c = Getc()) f = c == '-' ? -1 : 1; for (x = 0; c >= '0' && c <= '9'; c = Getc()) x = (x << 1) + (x << 3) + (c ^ 48); x *= f; } } using IO :: In; const int maxn(1 << 20 | 1); const int mod(998244353); const ll inf(1e18); struct Point { int x, y; inline Point(int _x = 0, int _y = 0) { x = _x, y = _y; } inline Point operator -(Point b) const { return Point(x - b.x, y - b.y); } inline ll operator *(Point b) const { return (ll)x * b.y - (ll)y * b.x; } } cur; int tp, m, mx, n, mxr; bitset <maxn> done; vector <Point> vc[maxn]; inline void Merge(int ls, int rs, int ff) { done[ff] = 1; int i, j, l1, l2, l3; vc[ff].clear(), l1 = vc[ls].size(), l2 = vc[rs].size(); for (i = j = l3 = 0; i < l1 || j < l2; ) { if (j == l2 || (i < l1 && (vc[ls][i].x < vc[rs][j].x || (vc[ls][i].x == vc[rs][j].x && vc[ls][i].y < vc[rs][j].y)))) { while (l3 > 1 && (vc[ff][l3 - 1] - vc[ff][l3 - 2]) * (vc[ls][i] - vc[ff][l3 - 2]) >= 0) vc[ff].pop_back(), --l3; vc[ff].push_back(vc[ls][i]), ++i, ++l3; } else { while (l3 > 1 && (vc[ff][l3 - 1] - vc[ff][l3 - 2]) * (vc[rs][j] - vc[ff][l3 - 2]) >= 0) vc[ff].pop_back(), --l3; vc[ff].push_back(vc[rs][j]), ++j, ++l3; } } } inline ll Calc(int x) { if (!vc[x].size()) return -inf; int l, r, mid, ans; ans = vc[x].size() - 1, l = 0, r = vc[x].size() - 2; while (l <= r) { mid = (l + r) >> 1; if ((vc[x][mid + 1] - vc[x][mid]) * cur >= 0) ans = mid, r = mid - 1; else l = mid + 1; } return Point(cur.x, cur.y) * vc[x][ans]; } void Insert(int x, int l, int r, int p) { int mid; if (l == r) { mxr = max(mxr, x); vc[x].push_back(cur), done[x] = 1; return; } mid = (l + r) >> 1; p <= mid ? Insert(x << 1, l, mid, p) : Insert(x << 1 | 1, mid + 1, r, p); if (x == (x & -x)) return; if (p == r && !done[x - 1]) Merge((x - 1) << 1, (x - 1) << 1 | 1, x - 1); } void Delete(int x, int l, int r, int p) { int mid; done[x] = 0; if (l == r) { mxr = max(mxr, x); vc[x].pop_back(); return; } mid = (l + r) >> 1; p <= mid ? Delete(x << 1, l, mid, p) : Delete(x << 1 | 1, mid + 1, r, p); } ll Query(int x, int l, int r, int ql, int qr) { int mid; ll ret; if (ql <= l && qr >= r && done[x]) return Calc(x); if (l == r) return -inf; ret = -inf, mid = (l + r) >> 1; if (ql <= mid) ret = Query(x << 1, l, mid, ql, qr); if (qr > mid) ret = max(ret, Query(x << 1 | 1, mid + 1, r, ql, qr)); return ret; } int main() { int i, op, x, y, l, r, ans; In(tp); while (In(m), m) { ans = n = 0, done.reset(); for (i = 1; i <= mxr; ++i) vc[i].clear(); for (mxr = 0, mx = 1; mx <= 300000; mx <<= 1); for (i = 1; i <= m; ++i) { In(op); if (op == 1) In(x), In(y), cur = Point(x, y), Insert(1, 1, mx, ++n); else if (op == 2) Delete(1, 1, mx, n), --n; else { In(l), In(r), In(x), In(y), cur = Point(x, y); ans ^= (Query(1, 1, mx, l, r) % mod + mod) % mod; } } printf("%d\n", ans); } return 0; }