题解:P11691 [Ynoi Hard Round 2025] 《十字神名的预言者》慈悲(色彩)

· · 题解

思路

注意到数据范围不大,考虑一些高复杂度的做法。

对每个点 P,把它对 m 个半平面的判定看成一个 01 串:第 j 位为 1 表示 P 不满足第 j 个半平面。那么询问 [l,r] 就是求在 [l,r] 内全为 0 的点的权值和。

考虑如何求出每个点是否属于半平面,把每 w = 64 个半平面分成一块,由于 B_j > 0,对于一个固定的 x,属于半平面的一定是一个前缀,即 y \le k。考虑求出 w 条直线在 x 时 y 的大小,排序后求出每个前缀对应的二进制状态。由于交点只有 O(w^2) 个,顺序只会变化 O(w^2) 次,每次交换两个数的位置,可以做到 O(w)。查询就二分。一个块的复杂度是 O(n \log w +w^3)。总复杂度是 O(\frac{nm\log w}{w}+mw^2)。

接下来考虑查询操作。把 B 个半平面分一块,记当前块为 [L,R],处理所有 l \in [L, R], r > R 的询问。对每个点记:

$v$:块右边第一个 $1$ 的位置。 这个点对询问 $[l, r]$ 有贡献等价于 $l > u$ 且 $r < v$。 于是按 $r$ 从右往左扫,求 $l > u$ 的点的和。那么有 $O(\frac{nm}{B})$ 次加法,$O(q)$ 次查询。对值域分块,值域只有 $O(B)$,容易做到 $O(1)-O(\sqrt{B})$。跨块的总操作次数是 $O(\frac{nm}{B}+q\sqrt{B})$。 接下来考虑在一个块内的所有询问。 对块内分治,设中点为 $mid$。对一个点权值为 $w$,找出覆盖 $mid,mid+1$ 的最长连续零段 $[a,b]$。若这两位中有 $1$,这个点就没有贡献。 询问 $[l,r]$ 的答案就是 $$ \sum_{[a,b]\supseteq[l,r]} w $$ 这个就做一遍二维前缀和即可求出。 但是还有一个问题,就是每层有 $n$ 个点要求 $a,b$,注意到属于半平面情况相同的点是本质相同的,可以合并。$k$ 个半平面最多有 $O(k^2)$ 个不同的点。记一个长度为 $B$ 块的操作次数是 $F(B)$。 有 $$ F(n)=2F(n/2)+O(n^2) $$ 所以一个块的操作次数是 $O(B^2+n)$ 的。块内总的操作次数是 $O(mB+\frac{nm}{B})$ 的。 总的操作次数是 $O(mB + \frac{nm}{B} + q\sqrt{B})$ 的,方便实现 $B = 256$。时间复杂度瓶颈就是处理出每个点属于哪些半平面。时间复杂度是 $O(\frac{nm\log w}{w}+mw^2)$。 ## 代码 ::::info[code] ```cpp #include<bits/stdc++.h> using namespace std; class Data { friend int main(); private: unsigned short x1,x2,x3,x4; public: void add(const Data &a,const Data &b); void sub(const Data &a,const Data &b); void clr(); }; namespace chesed { using ll = long long; using ull = unsigned long long; const int B = 256; struct Point { int x, y, id; bool operator < (const Point &p) const { return x < p.x; } }; struct Line { int a, b, c; }; struct Query { int l, r, id; bool operator < (const Query &p) const { return r > p.r; } }; using Mask = array<ull, 4>; struct Hash { size_t operator () (const Mask &a) const { ull h = 0; for (ull x : a) h = (h ^ x) * 11995408973635179863ull, h ^= h >> 31; return h; } }; using Table = unordered_map<Mask, Data, Hash>; inline void add(Data &a, const Data &b) { Data t; t.add(a, b); a = t; } inline void put(Table &f, const Mask &s, const Data &w) { auto [it, ok] = f.try_emplace(s); if (ok) it->second = w; else add(it->second, w); } inline int first(const Mask &s, int def) { for (int j = 0; j < 4; j ++) if (s[j]) return j * 64 + __builtin_ctzll(s[j]); return def; } inline int last(const Mask &s, int def) { for (int j = 3; j >= 0; j --) if (s[j]) return j * 64 + 63 - __builtin_clzll(s[j]); return def; } inline bool lower_line(const Line &u, const Line &v, int x) { ll h = (u.c - 1ll * u.a * x) * v.b; ll k = (v.c - 1ll * v.a * x) * u.b; if (h != k) return h < k; return 1ll * u.a * v.b > 1ll * v.a * u.b; } void build(const vector<Point> &p, const Line *a, int k, int id, vector<Mask> &s) { vector<int> ev; int ord[64]; ull pre[65], all = k == 64 ? ~0ull : (1ull << k) - 1; iota(ord, ord + k, 0); ev.push_back(p.front().x); for (int i = 0; i < k; i ++) for (int j = 0; j < i; j ++) { ll den = 1ll * a[i].a * a[j].b - 1ll * a[j].a * a[i].b; ll num = 1ll * a[i].c * a[j].b - 1ll * a[j].c * a[i].b; if (!den) continue; if (den < 0) den = -den, num = -num; ll x = num / den + (num % den > 0); if (x > p.front().x && x <= p.back().x) ev.push_back(x); } sort(ev.begin(), ev.end()); size_t pos = 0; for (int i = 0; i < (int)p.size(); i ++) { int x = p[i].x, y = p[i].y; if (pos < ev.size() && ev[pos] <= x) { while (pos < ev.size() && ev[pos] <= x) pos ++; for (int j = 1; j < k; j ++) { int v = ord[j], t = j; while (t && lower_line(a[v], a[ord[t - 1]], x)) ord[t] = ord[t - 1], t --; ord[t] = v; } pre[0] = 0; for (int j = 0; j < k; j ++) pre[j + 1] = pre[j] | (1ull << ord[j]); } int l = 0, r = k; while (l < r) { int mid = (l + r) >> 1; const Line &v = a[ord[mid]]; if (1ll * v.a * x + 1ll * v.b * y >= v.c) l = mid + 1; else r = mid; } s[i][id] = all ^ pre[l]; } } Data res[B][B]; void dfs(int l, int r, const Table &f) { if (l == r) { auto it = f.find(Mask{}); res[l][r].clr(); if (it != f.end()) res[l][r] = it->second; return; } int mid = (l + r) >> 1; Mask cut{}; for (int j = l; j <= mid; j ++) cut[j >> 6] |= 1ull << (j & 63); Table lf, rt; for (int i = l; i <= mid; i ++) for (int j = mid + 1; j <= r; j ++) res[i][j].clr(); for (const auto &e : f) { Mask a, b; for (int j = 0; j < 4; j ++) { a[j] = e.first[j] & cut[j]; b[j] = e.first[j] ^ a[j]; } put(lf, a, e.second); put(rt, b, e.second); int i = last(a, l - 1) + 1, j = first(b, r + 1) - 1; if (i <= mid && j > mid) add(res[i][j], e.second); } for (int i = l + 1; i <= mid; i ++) for (int j = mid + 1; j <= r; j ++) add(res[i][j], res[i - 1][j]); for (int i = l; i <= mid; i ++) for (int j = r - 1; j > mid; j --) add(res[i][j], res[i][j + 1]); dfs(l, mid, lf); dfs(mid + 1, r, rt); } } void solve(int n, int xy[][2], Data d[], int m, int abc[][3], int q, int lr[][2], Data ans[]) { using namespace chesed; vector<Point> p(n); for (int i = 0; i < n; i ++) p[i] = {xy[i + 1][0], xy[i + 1][1], i + 1}; sort(p.begin(), p.end()); vector<Line> a(m + 1); for (int i = 1; i <= m; i ++) a[i] = {abc[i][0], abc[i][1], abc[i][2]}; int nb = (m + B - 1) / B; vector<vector<Query>> in(nb), cr(nb); for (int i = 1; i <= q; i ++) { int l = lr[i][0], r = lr[i][1], b = (l - 1) / B; if ((r - 1) / B == b) in[b].push_back({l, r, i}); else cr[b].push_back({l, r, i}); } vector<Mask> s(n); vector<int> nxt(n, m + 1), lk(n), hd(m + 2); for (int b = nb - 1; b >= 0; b --) { int L = b * B + 1, len = min(B, m - L + 1); fill(s.begin(), s.end(), Mask{}); for (int j = 0; j < len; j += 64) build(p, &a[L + j], min(64, len - j), j >> 6, s); if (!in[b].empty()) { Table f; f.reserve(min(n, 1 + len * (len + 1) / 2)); for (int i = 0; i < n; i ++) put(f, s[i], d[p[i].id]); dfs(0, len - 1, f); for (Query t : in[b]) ans[t.id] = res[t.l - L][t.r - L]; } if (!cr[b].empty()) { fill(hd.begin(), hd.end(), -1); for (int i = 0; i < n; i ++) { lk[i] = hd[nxt[i]]; hd[nxt[i]] = i; } Data cnt[B], sum[B / 16]; for (auto &w : cnt) w.clr(); for (auto &w : sum) w.clr(); sort(cr[b].begin(), cr[b].end()); int pos = m + 1; for (Query t : cr[b]) { while (pos > t.r) { for (int i = hd[pos]; i != -1; i = lk[i]) { int j = last(s[i], -1) + 1; if (j == len) continue; add(cnt[j], d[p[i].id]); add(sum[j >> 4], d[p[i].id]); } pos --; } int k = t.l - L + 1, g = k >> 4; Data w; w.clr(); for (int j = 0; j < g; j ++) add(w, sum[j]); for (int j = g * 16; j < k; j ++) add(w, cnt[j]); ans[t.id] = w; } } for (int i = 0; i < n; i ++) { int j = first(s[i], len); if (j < len) nxt[i] = L + j; } } } ``` ::::