赛时 T2 看出来偏序,想到了分治,但没看出来三维偏序,更没想出来 CDQ 分治。
回来补坑了。
首先将同类项($a,b,c$ 都相等的元素)合并,并记录它出现的次数 $cnt$。
然后要统计三维偏序,这很头痛,所以考虑对每一维把它分别排序,并保证之前维度排序的有效性。
由于是统计点对之间的信息,可以联想到 CDQ 分治。
假设已知 solve(l, mid)
和 solve(mid + 1, r)
的答案,现在要计算两个区间之间的答案。
考虑在分治开始之前对 $a$ 排序,这样就只需要统计两维。
此时左右区间的 $a$ 一定是相对有序的,此时把它们分别对 $b$ 升序排序,然后用一个类似归并排序的双指针算法,保证 $b$ 相对有序。
接着把 $c$ 的出现次数加入到一个维护值域出现次数的数据结构里,例如树状数组。
这样就可以保证三维都是有序的。
顺带一提 T2,最内层是可以维护一个 set 二分的。
#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 15;
int n, Max, m;
struct node {
int a, b, c;
int cnt, ans;
} p[N], a[N];
bool cmp1(node a, node b) {
if (a.l != b.l) return a.l < b.l;
if (a.r != b.r) return a.r < b.r;
return a.f < b.f;
}
bool cmp2(node a, node b) {
if (a.r != b.r) return a.r < b.r;
return a.f < b.f;
}
void cdq(int l, int r) {
if (l == r) return;
int mid = l + r >> 1;
cdq(l, mid), cdq(mid + 1, r);
sort(a + l, a + 1 + mid, cmp2);
sort(a + mid + 1, a + r + 1, cmp2);
int j1 = l, j2 = mid + 1;
while (j2 <= r) {
while (j1 <= mid && a[j1].b <= a[j2].b) add(a[j1].c, a[j1].cnt), j1++;
a[j2].ans += query(a[j2].c);
j2++;
}
for (int i = l; i < j1; i++) add(a[i].c, -a[i].cnt);
}
int main() {
scanf("%d%d", &n, &Max);
for (int i = 1; i <= n; i++) scanf("%d%d%d", &p[i].l, &p[i].r, &p[i].f);
sort(p + 1, p + 1 + n, cmp1);
for (int i = 1; i <= n; i++) {
if (p[i].a == a[m].a && p[i].b == a[m].b && p[i].c == a[m].c) a[m].cnt++;
else a[++m] = p[i], a[m].cnt = 1;
}
cdq(1, m);
for (int i = 1; i <= m; i++) ans[a[i].ans + a[i].cnt - 1] += a[i].cnt;
for (int i = 0; i < n; i++) printf("%d\n", ans[i]);
return 0;
}