CDQ 优化 1D 动规转移。
首先容易写出一个 $O(n^2)$ 的 DP。
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 15;
int n, dp[N];
struct node {
int l, a, b, c;
} a[N];
bool cmp1(node a, node b) { return a.l < b.l; }
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; i++) scanf("%d%d%d%d", &a[i].l, &a[i].a, &a[i].b, &a[i].c);
sort(a + 1, a + 1 + n, cmp1);
dp[1] = 1;
for (int i = 2; i <= n; i++)
for (int j = 1; j < i; j++)
if (a[j].c <= a[i].a && a[i].c >= a[j].b) dp[i] = max(dp[i], dp[j] + 1);
int ans = 0;
for (int i = 1; i <= n; i++) ans = max(ans, dp[i]);
printf("%d\n", ans);
return 0;
}
然后这玩意儿乍看之下是一个四维偏序,但是再仔细一看,它其实是一个不同维度之间的三维偏序。
即先把总体对 $l$ 排序,然后分成左右两个区间,左边对 $c$ 升序,右边对 $a$ 升序排序。
至此,已经完成了 $l_j \lt l_i$ 和 $c_j \leq a_i$ 两维,前者是由于先前已经对 $l$ 升序排序;后者可以简单双指针维护。
还差一个 $b_j \leq c_i$?这也是简单的,直接树状数组即可。
当 $l=r$ 是递归边界,记得是和 $1$ 取 $\max$ 而不是赋值。
要递归完左边再递归右边,因为朴素 DP 就是从左往右做的,CDQ 当然也要左边做完再合并,然后再递归右边信息。
注意 $a,b,c \leq 10^9$,所以需要离散化,另外还需注意离散化不要 RE 了。
#include <bits/stdc++.h>
using namespace std;
const int N = 4e5 + 15;
int n, dp[N];
int p[N], m = 0;
struct node {
int l, a, b, c, id;
} a[N];
bool cmp1(node a, node b) {
return a.l < b.l;
}
bool cmp2(node a, node b) {
return a.c < b.c;
}
bool cmp3(node a, node b) {
return a.a < b.a;
}
int tr[N];
void add(int x, int d) {
for ( ; x < N; x += (x & -x)) tr[x] = max(tr[x], d);
}
int query(int x) {
int res = 0;
for ( ; x ; x -= (x & -x)) res = max(res, tr[x]);
return res;
}
void clr(int x) {
for ( ; x < N; x += (x & -x)) tr[x] = 0;
}
void cdq(int l, int r) {
if (l == r) {
dp[a[l].id] = max(dp[a[l].id], 1);
return;
}
int mid = l + r >> 1;
sort(a + l, a + r + 1, cmp1);
cdq(l, mid);
sort(a + l, a + mid + 1, cmp2);
sort(a + mid + 1, a + r + 1, cmp3);
for (int i = l, j = mid + 1, k = l; k <= r; k++) {
if ((j > r) || (i <= mid && a[i].c <= a[j].a)) add(a[i].b, dp[a[i].id]), i++;
else dp[a[j].id] = max(dp[a[j].id], query(a[j].c) + 1), j++;
}
for (int i = l; i <= mid; i++) clr(a[i].b);
cdq(mid + 1, r);
}
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; i++) scanf("%d%d%d%d", &a[i].l, &a[i].a, &a[i].b, &a[i].c), a[i].id = i;
for (int i = 1; i <= n; i++) p[++m] = a[i].a, p[++m] = a[i].b, p[++m] = a[i].c;
sort(p + 1, p + 1 + m);
m = unique(p + 1, p + 1 + m) - p - 1;
for (int i = 1; i <= n; i++) {
a[i].a = lower_bound(p + 1, p + 1 + m, a[i].a) - p;
a[i].b = lower_bound(p + 1, p + 1 + m, a[i].b) - p;
a[i].c = lower_bound(p + 1, p + 1 + m, a[i].c) - p;
}
sort(a + 1, a + 1 + n, cmp1);
cdq(1, n);
int ans = 0;
for (int i = 1; i <= n; i++) ans = max(ans, dp[i]);
printf("%d\n", ans);
return 0;
}