本题要求恰好有 k 个 1 的子矩形数量,我们将当前矩形劈成两半(以劈成左一半和右一半为例),那么符合条件的子矩形要么在左半,要么在右半,要么跨越中线。
考虑跨越中线的如何合并。我们枚举子矩形的上下边界,然后开个桶 p 统计左半矩形所含 1 数量小于 i 时左边界的最小值(右半矩形同理),然后直接枚举左半边的 1 的个数就可以统计了。代码。
CDQ 分治
树套树的本质作用是降维,但是如果允许离线,则可以使用 CDQ 分治高效地完成这个问题。
最重要的应用是解决三维偏序问题。分别处理三个信息。第一维可以将原数组按照 x 排序,xi≤xj 转化为 i<j。注意此时如果数都相同会出问题,因此去个重。第二维可以在分治时采用类似于归并排序的方式解决(求正序对),不过由于第三维的限制,并不是所有的信息都可以加到答案里的,需要整一个权值树状数组来处理第三维的信息:左半段序列的信息加入树状数组,右半段信息进行统计。代码。
#include<bits/stdc++.h> usingnamespace std; constint N = 4e5 + 5;
int n, m, lsh[N * 2], C[N * 2], ans[N]; structNode { int a, b, c, d, id, op; booloperator< (const Node a) const { if (this->a != a.a) returnthis->a < a.a; return id < a.id; } } a[N * 2], b[N * 2], T[N * 2]; inlinevoidadd(int x, int k){ for (; x <= n; x += x & -x) C[x] += k; } inlineintsum(int x){ int r = 0; for (; x; x -= x & -x) r += C[x]; return r; }
voidcdq2(int l, int r){ if (l == r) return; int mid = l + r >> 1; cdq2(l, mid); cdq2(mid + 1, r); for (int i = l, p = l, q = mid + 1; i <= r; ++i) { if (p <= mid && (q > r || b[p].c <= b[q].c)) { if (b[p].op == 0 && b[p].id == 0) add(b[p].d, 1); ++p; } else { if (b[q].op == 1 && b[q].id) ans[b[q].id] += sum(b[q].d); ++q; } } for (int i = l; i <= mid; ++i) if (b[i].op == 0 && b[i].id == 0) add(b[i].d, -1); merge(b + l, b + mid + 1, b + mid + 1, b + r + 1, T + l, [](Node x, Node y) { return x.c != y.c ? x.c < y.c : x.id < y.id; }); for (int i = l; i <= r; ++i) b[i] = T[i]; // sort(b + l, b + r + 1, [](Node x, Node y) { return x.c != y.c ? x.c < y.c : x.id < y.id; }); } voidcdq(int l, int r){ if (l == r) return; int mid = l + r >> 1; cdq(l, mid); cdq(mid + 1, r); for (int i = l; i <= mid; ++i) a[i].op = 0; // a 小的左半部分产生贡献 for (int i = mid + 1; i <= r; ++i) a[i].op = 1; // a 大的右半部分计算答案 merge(a + l, a + mid + 1, a + mid + 1, a + r + 1, T + l, [](Node x, Node y) { return x.b != y.b ? x.b < y.b : x.id < y.id; }); // 按照 b 排序,进行 CDQ 分治计算答案 for (int i = l; i <= r; ++i) a[i] = b[i] = T[i];
// sort(a + l, a + r + 1, [](Node x, Node y) { return x.b != y.b ? x.b < y.b : x.id < y.id; }); // for (int i = l; i <= r; ++i) b[i] = a[i];
cdq2(l, r); }
intmain(void){ ios::sync_with_stdio(0); cin >> n >> m; for (int i = 1; i <= n + m; ++i) { cin >> a[i].a >> a[i].b >> a[i].c >> a[i].d; a[i].c *= -1; a[i].d *= -1; lsh[i] = a[i].d; if (i > n) a[i].id = i - n; } n += m; sort(a + 1, a + n + 1); sort(lsh + 1, lsh + n + 1); for (int i = 1; i <= n; ++i) a[i].d = lower_bound(lsh + 1, lsh + n + 1, a[i].d) - lsh; cdq(1, n); for (int i = 1; i <= m; ++i) cout << ans[i] << '\n'; return0; }