题意:
题目链接:http://acm.split.hdu.edu.cn/showproblem.php?pid=5618
给出n个有序对(a,,b,c),对于每个(ai,bi,ci)找到满足aj <=ai,bj<=bi,cj<=ci的有序对有多少个。
思路:
典型的三维偏序问题,可以用CDQ分治来解决。
首先将有序对按照a的大小排序,这样对区间[L,R)进行分治的时候就可以默认左半部分[L,M)的a要小于等于右半部份[M,R)的a。按照CDQ分治的思想,先递归处理左右两个部分的子问题,然后处理左半部分对于右半部份的影响,就这题来说,就是对左右两部分按照b的大小排序,按照归并排序的写法先进入临时数组tmp的有序对b一定不大于之后进的。那么现在关键是处理c,因为有c存在不能直接计数,注意到所有数字的大小只有1e5,这时候考虑用树状数组求和来统计c的个数,就如同求逆序对的作法一样。对于左半部分的每一个有序对在来更新树状数组,右半部份的每个有序对来查询树状数组,从而计数。
这题还有几个地方需要注意:
1.定义Query大小关系的时候,要记得包括小于等于的情况。
2.排序的时候不能只按照a的大小排序,还要按照先b后c确定优先级,因为这样才能保证右边区间的有序对能找到所有比优先级他小的。
3.按照上面的做法,如果有m个完全一样的有序对(a,b,c),这m个有序对所求的结果受到位置的影响并不一样,因为排在后面的可以统计前面的,而前面的却没有统计后面的,所以一开始排序排在m个中最后的那个有序对才是这m个有序对共同的答案。
4.记得在每一次的分治都要清空树状数组。
代码:
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 1e5 + 10;
struct node {
int x, y, z, id;
} a[MAXN], b[MAXN];
bool cmp1(const node &aa, const node &bb) {
if (aa.x != bb.x) return aa.x < bb.x;
return (aa.y < bb.y) || (aa.y == bb.y && aa.z < bb.z);
}
bool cmp2(const node &aa, const node &bb) {
return (aa.y < bb.y) || (aa.y == bb.y && aa.z < bb.z);
}
int C[MAXN], ans[MAXN];
void add(int x, int y) {
while (x < MAXN) {
C[x] += y;
x += x & -x;
}
}
int sum(int x) {
int res = 0;
while (x) {
res += C[x];
x -= x & -x;
}
return res;
}
void cle(int x) {
while (x < MAXN) {
C[x] = 0;
x += x & -x;
}
}
void solve(int l, int r) {
if (l == r) return;
int m = (l + r) >> 1;
solve(l, m);
for (int i = l; i <= r; i++) b[i] = a[i];
sort (b + l, b + m + 1, cmp2);
sort (b + m + 1, b + r + 1, cmp2);
for (int j = m + 1, i = l; j <= r; j++) {
while (i <= m && b[i].y <= b[j].y) {
add(b[i].z, 1); ++i;
}
ans[b[j].id] += sum(b[j].z);
}
for (int i = l; i <= m; i++) cle(b[i].z);
solve(m + 1, r);
}
int main() {
//freopen("in.txt", "r", stdin);
int T;
scanf("%d", &T);
while (T--) {
int n;
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
scanf("%d%d%d", &a[i].x, &a[i].y, &a[i].z);
a[i].id = i;
}
sort (a + 1, a + 1 + n, cmp1);
memset(C, 0, sizeof(C));
memset(ans, 0, sizeof(ans));
solve(1, n);
for (int i = n - 1; i >= 1; i--) {
if (a[i].x == a[i + 1].x && a[i].y == a[i + 1].y && a[i].z == a[i + 1].z) {
ans[a[i].id] = ans[a[i + 1].id];
}
}
for (int i = 1; i <= n; i++)
printf("%d\n", ans[i]);
}
return 0;
}