传送门
三维LIS。即每个点有个三维坐标,两个点能放在一前一后当且仅当$$x_i < x_j, y_i < y_j, z_i < z_j$$,求最长的序列,并该条件下的方案数。
网络赛的题,比赛的时候一想二维的,直接上了KD树就过了。
赛后听别人说,都是树套树或者CDQ分治的。于是就去cxlove大大的博客学习了一下CDQ分治。
这种分治的大致思路是每次把区间分两半,先做左边,然后把左边对右边的影响(在这题里相当于dp值的转移)计算好,然后再右边递归下去算。
这种复杂度很容易计算,T(n) = 2T(n / 2) + O(xxxn),最终复杂度肯定是xxxn lg n(想想每个元素被碰的次数就好了)。
我目前对这种分治的理解大致有这么两条:
-
在第i号位置的元素其实被更新了popcount(i)次(popcount表示这个值在二进制下包含的1的个数),就是二进制下看,每有一个1,就会被对应长度的块更新一次。
-
若i < j,则在通过f[i]影响f[j]的时候,f[i]总是已经计算好了的。
在这题里显然先对x排序,然后变成一个二维查询的问题,利用这个思想结合树状数组就可以达到$$O(n lg^2 n)$$的复杂度。
KD树的做法就更为直观,直接按x排序后,动态求左上角的那个块的值就好了。大概是写得太奔放的原因,我的KD树比这个分治的要快
今天写的时候WA了好久,原因有两个
-
分治的时候sort了原数组在处理后要还原回去。
-
我是每个区间分别离散化的,结果离散化了以后,被离散化的值没有还原回去,导致在别的区间内再离散化的的时候,值的大小相对关系就不对了…
分治的代码:
#include <cstdio>
#include <cstring>
#include <set>
#include <map>
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;
#define rep(i,n) for (int i = 0; i < (int)(n); i++)
#define foreach(it,v) for (__typeof((v).end()) it = (v).begin(); it != (v).end(); it++)
typedef pair <int, unsigned int> PII;
const int N = 100005;
const unsigned int Mod = 1 << 30;
struct Point {
int x, y, z, nz;
PII res;
void read() { scanf("%d%d%d", &x, &y, &z); }
}a[N];
PII Tr[N];
bool cmpx(Point a, Point b) {
return a.x < b.x || (a.x == b.x && a.y < b.y) || (a.x == b.x && a.y == b.y && a.z < b.z);
}
bool cmpy(Point a, Point b) {
return a.y < b.y || (a.y == b.y && a.z < b.z);
}
void up(PII &r, PII x) {
if (x.first > r.first)
r = x;
else if (x.first == r.first)
r.second += x.second;
}
void add(int i, int n, PII c) {
for (i++, n++; i < n; i += i & -i)
up(Tr[i], c);
}
PII get(int i) {
PII res(0, 0);
for (i++; i; i -= i & -i)
up(res, Tr[i]);
return res;
}
void gao(int l, int r) {
if (l + 1 >= r) return;
int mid = (l + r) / 2;
gao(l, mid);
sort(a + l, a + mid, cmpy);
sort(a + mid, a + r, cmpy);
vector <int> v;
for (int i = l; i < r; i++) v.push_back(a[i].z);
sort(v.begin(), v.end());
for (int i = l; i < r; i++) a[i].nz = lower_bound(v.begin(), v.end(), a[i].z) - v.begin();
fill(Tr + 1, Tr + r - l + 1, PII(0, 0));
for (int j = l, i = mid; i < r; i++) {
while (j < mid && a[j].y <= a[i].y) {
add(a[j].nz, r - l, a[j].res);
j++;
}
PII cur = get(a[i].nz);
cur.first++;
up(a[i].res, cur);
}
sort(a + mid, a + r, cmpx);
gao(mid, r);
}
int main() {
int Tc;
scanf("%d", &Tc);
while (Tc--) {
int n;
scanf("%d", &n);
rep (i, n) a[i].read();
rep (i, n) a[i].res = PII(1, 1);
sort(a, a + n, cmpx);
gao(0, n);
PII res = PII(0, 1);
rep (i, n) up(res, a[i].res);
cout << res.first << " " << (res.second & (Mod - 1)) << endl;
}
}
KD树的代码:
#include <cstdio>
#include <cstring>
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;
#define rep(i,n) for (int i = 0; i < (int)(n); i++)
typedef pair <int, int> PII;
typedef unsigned int ui;
const int N = 100005;
const int INF = 0x7FFFFFFF;
const ui mask = (1 << 30) - 1;
int Tc, n, m;
struct Node {
PII e, sub, cur;
int o;
Node *lc, *rc;
}*C, pool[N];
struct TPoint {
int x, y, z;
TPoint() {}
TPoint(int x, int y, int z): x(x), y(y), z(z) {}
}a[N];
PII b[N];
bool cmp(const TPoint &a, const TPoint &b) {
return a.x < b.x || (a.x == b.x && a.y < b.y) || (a.x == b.x && a.y == b.y && a.z < b.z);
}
bool cmpX(const PII &a, const PII &b) {
return a.first < b.first || (a.first == b.first && a.second < b.second);
}
bool cmpY(const PII &a, const PII &b) {
return a.second < b.second || (a.second == b.second && a.first < b.first);
}
Node *build(PII *b, int l, int r, int o) {
if (l >= r) return NULL;
Node *p = C++;
p->o = o;
int mid = (l + r) / 2;
nth_element(b + l, b + mid, b + r, o ? cmpY : cmpX);
p->e = b[mid];
p->cur = p->sub = PII(0, 0);
p->lc = build(b, l, mid, o ^ 1);
p->rc = build(b, mid + 1, r, o ^ 1);
return p;
}
inline void update(PII &cur, const PII &v) {
if (v.first > cur.first) {
cur = v;
} else if (cur.first == v.first) {
cur.second = cur.second + v.second;
}
}
void add(Node *p, const PII &e, const PII &v) {
update(p->sub, v);
if (e == p->e) {
update(p->cur, v);
return;
} else {
bool c = p->o ? cmpY(e, p->e) : cmpX(e, p->e);
if (c) {
add(p->lc, e, v);
} else {
add(p->rc, e, v);
}
}
}
PII ans;
void get(Node *p, const PII &e, int maxx = INF, int maxy = INF) {
if (!p) return;
if (p->sub.first < ans.first) return;
if (maxx <= e.first && maxy <= e.second) {
update(ans, p->sub);
} else {
if (p->e.first <= e.first && p->e.second <= e.second) update(ans, p->cur);
if (p->o) {
if (p->e.second <= e.second) get(p->rc, e, maxx, maxy);
get(p->lc, e, maxx, min(maxy, p->e.second));
} else {
if (p->e.first <= e.first) get(p->rc, e, maxx, maxy);
get(p->lc, e, min(maxx, p->e.first), maxy);
}
}
}
int main() {
#ifdef cwj
freopen("E.in", "r", stdin);
#endif
scanf("%d", &Tc);
rep (ri, Tc) {
scanf("%d", &n);
rep (i, n) {
scanf("%d%d%d", &a[i].x, &a[i].y, &a[i].z);
b[i] = make_pair(a[i].y, a[i].z);
}
sort(b, b + n);
m = unique(b, b + n) - b;
C = pool;
Node *root = build(b, 0, m, 0);
sort(a, a + n, cmp);
rep (i, n) {
ans = PII(0, 0);
get(root, PII(a[i].y, a[i].z));
PII cur;
if (ans.first == 0) {
cur.first = 1;
cur.second = 1;
} else {
cur.first = ans.first + 1;
cur.second = ans.second;
}
// printf("cur %d %d %d\n", i, cur.first, cur.second);
add(root, PII(a[i].y, a[i].z), cur);
}
printf("%d %d\n", root->sub.first, root->sub.second & mask);
}
return 0;
}