1 条题解
-
0
当 时,新的凸包必然来源于原来的凸包,或者剥掉原来凸包后新的点集的凸包。
递归地做这个事情,如果最上面一层没有点没删,则停止递归,否则处理出下面一层的凸包后,处理出删掉这层的点后会露出的部分,将这一部分从下面分开来,连到上面即可。由于每层都至少要删一个点,所以只要预处理 层的凸包即可。
把凸包分成上下凸壳,并钦定一边可以平行于 轴,面积即为上下凸壳的面积和。用线段树维护每一层的凸壳,pushup 的时候加上中间的面积。
找露出的部分,即找点到凸包的切线,可以二分。取中间的线段,判断方向后往对应方向递归即可。左右切到一个点的时候要特判凸性。
剪切,拼接的部分可以直接用线段树分裂和合并,由于对应的 坐标是不交的,所以每次操作复杂度都是严格 。为了处理多次询问,可以用可持久化的思路,不对原来的凸壳做实质性修改,询问完毕后将多产生的点直接删除即可。
复杂度 。
如果在 uoj 上 97 分,检查有没有判:
- 输入 加上 爆 int;
- 输入 为 ;
- 输入 解密后有重复。
代码:https://uoj.ac/submission/653541
/* name: P3827 * author: 5ab * created at: 2023-09-06 */ #pragma GCC optimize("Ofast","unroll-loops") #pragma GCC target("sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx") #include <iostream> #include <algorithm> #include <numeric> #include <cassert> #include <vector> #include <tuple> using namespace std; #define all(x) (x).begin(), (x).end() #define ssz(x) (int((x).size())) auto chmax = [](auto& x, auto y) { if (x < y) x = y; }; auto chmin = [](auto& x, auto y) { if (x > y) x = y; }; using ll = long long; const int max_n = 1e5, max_lgn = 17, max_k = 101, max_s = (max_lgn + 1) * 2 * (max_n + 5 * max_k); struct point { int x, y; point operator-(const point& rhs) const { return point{ x - rhs.x, y - rhs.y }; } bool operator<(const point& rhs) const { return x == rhs.x ? y < rhs.y : x < rhs.x; } }; inline ll cross(const point& p, const point& q) { return 1ll * p.x * q.y - 1ll * p.y * q.x; } struct node { int ls, rs, lx, rx, siz; ll sm; } tr[max_s]; int ind = 0, n; int nnode() { return ind++; } int clone(int x) { tr[ind] = tr[x]; return ind++; } inline int glx(int x) { return tr[x].lx; } inline int grx(int x) { return tr[x].rx; } inline ll gsm(int x) { return tr[x].sm; } inline int gsz(int x) { return x == -1 ? 0 : tr[x].siz; } vector<int> cv; struct Hull { point a[max_n]; int ord[max_n], cl[max_n]; vector<int> rt; void pushup(int id) { if (tr[id].ls == -1) { int tmp = tr[id].rs; tr[id] = tr[tr[id].rs]; tr[id].ls = -1, tr[id].rs = tmp; } else if (tr[id].rs == -1) { int tmp = tr[id].ls; tr[id] = tr[tr[id].ls]; tr[id].rs = -1, tr[id].ls = tmp; } else { tr[id].lx = glx(tr[id].ls), tr[id].rx = grx(tr[id].rs); // cerr << id << " " << glx(tr[id].rs) << " + " << grx(tr[id].ls) << " " // << cross(a[glx(tr[id].rs)], a[grx(tr[id].ls)]) << " " << gsm(tr[id].ls) << " " << gsm(tr[id].rs) << endl; tr[id].sm = gsm(tr[id].ls) + gsm(tr[id].rs) + cross(a[glx(tr[id].rs)], a[grx(tr[id].ls)]); tr[id].siz = gsz(tr[id].ls) + gsz(tr[id].rs); } } int build(int l, int r, int ql, int qr) { if (ql >= qr) return -1; int id = nnode(); if (l == r) { tr[id] = { -1, -1, l, l, 1, 0 }; return id; } int mid = (l + r) >> 1, dfx = upper_bound(all(cv), mid) - begin(cv); tr[id].ls = build(l, mid, ql, dfx); tr[id].rs = build(mid + 1, r, dfx, qr); pushup(id); // cerr << id << " " << l << " " << r << " " << ssz(cv) << " " << tr[id].ls << " " << tr[id].rs << endl; return id; } void init(point *s) { copy(s, s + n, a); sort(a, a + n); fill(cl, cl + n, -1); for (int i = 0; i < n; i++) ord[i] = lower_bound(a, a + n, s[i]) - a; for (int _ = 0; _ <= max_k; _++) { cv.clear(); for (int i = 0; i < n; i++) if (cl[i] == -1) { while (ssz(cv) > 1 && cross(a[cv.back()] - a[end(cv)[-2]], a[i] - a[cv.back()]) > 0) cv.pop_back(); cv.push_back(i); } for (int x : cv) cl[x] = _; if (cv.empty()) break; rt.push_back(build(0, n - 1, 0, ssz(cv))); // cerr << _ << " " << rt[_] << ": "; // for (int x : cv) // cerr << x << " "; // cerr << endl; } } int findl(int sx, int id) { int l = 0, r = n - 1; while (l < r) { int mid = (l + r) >> 1; if (tr[id].ls == -1) id = tr[id].rs, l = mid + 1; else if (tr[id].rs == -1) id = tr[id].ls, r = mid; else if (glx(tr[id].rs) > sx || cross(a[glx(tr[id].rs)] - a[grx(tr[id].ls)], a[sx] - a[grx(tr[id].ls)]) > 0) id = tr[id].ls, r = mid; else id = tr[id].rs, l = mid + 1; } // cerr << sx << " " << id << " " << l << endl; return l > sx ? -1 : l; } int findr(int sx, int id) { int l = 0, r = n - 1; while (l < r) { int mid = (l + r) >> 1; // cerr << "findr: " << sx << " " << id << " " << l << " " << r << endl; if (tr[id].ls == -1) id = tr[id].rs, l = mid + 1; else if (tr[id].rs == -1) id = tr[id].ls, r = mid; else if (grx(tr[id].ls) < sx || cross(a[grx(tr[id].ls)] - a[glx(tr[id].rs)], a[sx] - a[glx(tr[id].rs)]) < 0) id = tr[id].rs, l = mid + 1; else id = tr[id].ls, r = mid; } return l < sx ? n : l; } int getrk(int x, int id) { int l = 0, r = n - 1, csiz = 0; while (l < r) { int mid = (l + r) >> 1; if (x <= mid) r = mid, id = tr[id].ls; else csiz += gsz(tr[id].ls), l = mid + 1, id = tr[id].rs; } return csiz; } int fndbyrk(int rk, int id) { // cerr << "fndrk: " << rk << " " << tr[id].siz << endl; int l = 0, r = n - 1; while (l < r) { int mid = (l + r) >> 1; if (gsz(tr[id].ls) > rk) r = mid, id = tr[id].ls; else l = mid + 1, rk -= gsz(tr[id].ls), id = tr[id].rs; } return l; } int split(int &x, int L, int R, int l, int r) { // cerr << "split: " << x << " " << L << " " << R << " " << l << " " << r << endl; if (x == -1) return -1; if (L <= l && r <= R) { int tmp = x; x = -1; return tmp; } // cerr << tr[x].ls << " " << tr[x].rs << endl; x = clone(x); int mid = (l + r) >> 1, yl = -1, yr = -1; if (L <= mid) yl = split(tr[x].ls, L, R, l, mid); if (mid < R) yr = split(tr[x].rs, L, R, mid + 1, r); if (yl == -1 && yr == -1) return -1; int y; if (tr[x].ls == -1 && tr[x].rs == -1) y = x, x = -1; else y = nnode(), pushup(x); tr[y].ls = yl, tr[y].rs = yr; pushup(y); // cerr << l << " " << r << ": " << x << " " << y << endl; return y; } void merge(int &x, int y) { if (x == -1 || y == -1) { x = x + y + 1; return; } x = clone(x); merge(tr[x].ls, tr[y].ls); merge(tr[x].rs, tr[y].rs); pushup(x); } ll solve(vector<int> dc) { for (int &x : dc) x = ord[x]; sort(all(dc), [&](int x, int y) { return cl[x] == cl[y] ? x < y : cl[x] > cl[y]; }); int st = ind; vector<int> nrt(size(rt)); for (int i = 0; i < ssz(rt); i++) nrt[i] = clone(rt[i]); for (int x : dc) if (cl[x] != -1) { int &xt = nrt[cl[x]]; int crk = getrk(x, xt), lp = 0, rp = n - 1, px = -1; // cerr << x << " " << cl[x] << " " << xt << " " << gsz(xt) << endl; // cerr << crk << endl; if (cl[x] < ssz(rt) - 1 && nrt[cl[x] + 1] != -1) { int pre = -1, nxt = -1; int &yt = nrt[cl[x] + 1]; if (crk > 0) lp = findr(pre = fndbyrk(crk - 1, xt), yt); if (crk < gsz(xt) - 1) rp = findl(nxt = fndbyrk(crk + 1, xt), yt); // cerr << pre << " " << nxt << " " << lp << " " << rp << " " << cross(a[nxt] - a[lp], a[lp] - a[pre]) << endl; if (lp < rp || (lp == rp && (pre == -1 || nxt == -1 || cross(a[nxt] - a[lp], a[lp] - a[pre]) >= 0))) { // cerr << "split " << yt << " into: " << lp << " " << rp << endl; px = split(yt, lp, rp, 0, n - 1); } } split(xt, x, x, 0, n - 1); merge(xt, px); } ll ans = tr[nrt[0]].sm; ind = st; return ans; } } U, D; point a[max_n]; signed main() { ios_base::sync_with_stdio(false); cin.tie(nullptr); int m; cin >> n >> m; for (int i = 0; i < n; i++) cin >> a[i].x >> a[i].y; U.init(a); for (int i = 0; i < n; i++) a[i].x *= -1, a[i].y *= -1; D.init(a); // cerr << clock() << endl; vector<int> ps; int k, lastans = -1; while (m--) { cin >> k; ps.resize(k); for (int i = 0; i < k; i++) { cin >> ps[i]; ps[i] = (1ll * ps[i] + lastans + n) % n; // cerr << ps[i] << endl; } sort(all(ps)); ps.erase(unique(all(ps)), end(ps)); ll ans = U.solve(ps) + D.solve(ps); cout << ans << "\n"; lastans = ans % n; } return 0; } // started coding at: 09-06 09:13:03
- 1
信息
- ID
- 6616
- 时间
- 3000ms
- 内存
- 768MiB
- 难度
- 10
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者