陌上花开 CDQ分治

题意:

有$n$个元素,第$i$个元素有$a_{i},b_{i},c_{i}$三个属性,设$f(i)$表示满足 $a_j \leq a_i,b_j \leq b_i,c_j \leq c_i$
对于 $d \in [0, n)$求$f(i) = d$的数量

三位偏序模板题。

  • 1、将已经读入好的数据按照某关键字排序
  • 2、设当前区间为[l,r],递归处理左区间[l,mid]和右区间[mid+1,r],计算左区间的修改操作对右区间的影响(一般用树状数组等数据结构维护)
  • 3、清除数据结构内的修改数据

是CDQ分治的经典题型

通过对$a_i$的排序,可以把其降低为二维,所以对一个元素,比他都小的元素必定在他之后。
对于$(l,r)$区间,我们分成$(l,mid),(mid+1,r)$ 左区间$a_i$一定比右区间小。然后我们对两个区间进行二维约束的排序。
计算左边区间对右边区间的贡献,保证了一维二维的条件下,只要用线段树查询满足条件左区间$c_i$的数量就好了。

代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
#define lowbit(i) i &(-i)
const int N = 1e5 + 10;
int tr[N << 2], k, v[N << 2];
void update(int x, int w)
{
while (x <= k)
{
tr[x] += w;
x += lowbit(x);
}
return;
}
int quary(int x)
{
int sum = 0;
while (x)
{
sum += tr[x];
x -= lowbit(x);
}
return sum;
}
struct node
{
int a, b, c, num, ans;
} st[N], p[N];
bool cmpx(node x, node y)
{
if (x.a == y.a)
return x.b == y.b ? x.c < y.c : x.b < y.b;
return x.a < y.a;
}
bool cmpy(node x, node y)
{
return x.b == y.b ? x.c < y.c : x.b < y.b;
}
int n, s, cnt;
void cdq(int l, int r)
{
if (l == r)
return;
int mid = (l + r) >> 1;
cdq(l, mid);
cdq(mid + 1, r);
sort(p + l, p + mid + 1, cmpy);
sort(p + mid + 1, p + r + 1, cmpy);
int pl = l, pr = mid + 1;
while (pr <= r)
{
while (pl <= mid && p[pl].b <= p[pr].b)
update(p[pl].c, p[pl].num), pl++;
p[pr].ans += quary(p[pr].c);
pr++;
}
for (int i = l; i < pl; i++)
update(p[i].c, -p[i].num);
}
int main()
{
scanf("%d%d", &n, &k);
for (int i = 1; i <= n; i++)
scanf("%d%d%d", &st[i].a, &st[i].b, &st[i].c);
sort(1 + st, 1 + st + n, cmpx);
cnt = 0;
s = 0;
for (int i = 1; i <= n; i++)
{
s++;
if (st[i].a != st[i + 1].a || st[i].b != st[i + 1].b || st[i].c != st[i + 1].c)
{
p[++cnt] = st[i];
p[cnt].num = s;
s = 0;
}
}
cdq(1, cnt);
for (int i = 1; i <= cnt; i++)
{
v[p[i].ans + p[i].num - 1] += p[i].num;
}
for (int i = 0; i < n; i++)
printf("%d\n", v[i]);
}