考虑一个朴素的DP方案:
先求出所有纸箱的最短边(以下第
i
个纸箱的最短边记为
这样,就变成了一个求最长严格上升子序列的问题,即
f[i]
为到了第
i
个纸箱,且必须选第
f[i]=1+maxxj<xi,yj<yi,zj<zif[j]
但这样显然是
O(n2)
的,TLE。考虑优化。
考虑一般的LIS优化:一般的LIS,可以将序列离散化之后,用树状数组进行优化。但这里有
y
和
还是先离散化序列,设有一个平面,把每一个决策都看作一个点
(yi,zi)
,权值为
f[i]
。那么在不考虑
xi
相等的情况下,
求
maxxj<xi,yj<yi,zj<zif[j]
就是求点
(yi−1,zi−1)
的左下角的点的最大权值。决策结束之后,还需要把点
(yi,zi)
插入平面。因此,考虑CDQ分治,就能得到
O(nlog2n)
的算法。但这与普通的CDQ分治有两点区别:
1、将
[l,r]
操作区间分成两个小的操作区间时,有时不能取中点作为分界点。假设分界点为
mid
,分成了
[l,mid]
和
[mid+1,r]
两个小操作区间,那么绝对不能有
xmid=xmid+1
,否则有可能使
f[i]
从满足
xj=xi
的
j
<script type="math/tex" id="MathJax-Element-28">j</script>转移过来。
2、必须要先递归左子区间,处理完左子区间对右子区间的贡献后再递归右子区间。
代码:
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
inline int read() {
int res = 0; bool bo = 0; char c;
while (((c = getchar()) < '0' || c > '9') && c != '-');
if (c == '-') bo = 1; else res = c - 48;
while ((c = getchar()) >= '0' && c <= '9')
res = (res << 3) + (res << 1) + (c - 48);
return bo ? ~res + 1 : res;
}
const int N = 2e5 + 5;
int A, ZZQ, n, orz[N][4], tot, T[N], tm[N], to[N];
struct cyx {
int x, y, z, ans;
cyx() {}
cyx(int _x, int _y, int _z) :
x(_x), y(_y), z(_z) {}
friend inline bool operator < (cyx a, cyx b) {
if (a.y != b.y) return a.y < b.y;
return a.z < b.z;
}
} a[N], c[N];
inline bool comp(const cyx &a, const cyx &b) {
if (a.x != b.x) return a.x < b.x;
if (a.y != b.y) return a.y < b.y;
return a.z < b.z;
}
inline bool comp1(const cyx &a, const cyx &b) {
return a.x < b.x;
}
inline bool comp2(const cyx &a, const cyx &b) {
return a.y < b.y;
}
inline bool comp3(const cyx &a, const cyx &b) {
return a.z < b.z;
}
void orzdalao(int x) {
for (; x <= n * 3; x += x & -x)
if (T[x] != 0) T[x] = 0;
else return;
}
void change(int x, int v) {
for (; x <= n * 3; x += x & -x)
T[x] = max(T[x], v);
}
int ask(int x) {
int res = 0;
for (; x; x -= x & -x)
res = max(res, T[x]);
return res;
}
void czk_is_so_weak(int l, int r) {
if (l == r) return;
int i, mid = l + r >> 1, p1 = mid, p2 = mid + 1;
if (a[p1].x == a[p2].x) {
while (p1 >= l && a[p1].x == a[mid].x) p1--;
while (p2 <= r && a[p2].x == a[mid + 1].x) p2++; p2--;
if (p1 == l - 1 && p2 == r) return;
if (p1 == l - 1) mid = p2; else if (p2 == r) mid = p1;
else {
int x = abs((r - p1) - (p1 - l + 1)),
y = abs((r - p2) - (p2 - l + 1));
mid = x < y ? p1 : p2;
}
}
czk_is_so_weak(l, mid); sort(a + l, a + mid + 1);
sort(a + mid + 1, a + r + 1); p1 = l; p2 = mid + 1;
for (i = l; i <= r; i++)
if (p2 > r || (p1 <= mid && a[p1].y < a[p2].y))
change(a[p1].z, a[p1].ans), p1++;
else a[p2].ans = max(a[p2].ans, ask(a[p2].z - 1) + 1), p2++;
for (i = l; i <= mid; i++) orzdalao(a[i].z);
sort(a + mid + 1, a + r + 1, comp);
czk_is_so_weak(mid + 1, r);
}
int main() {
freopen("box.in", "r", stdin);
freopen("box.out", "w", stdout);
int i, j, nxt, now = 1; A = read(); ZZQ = read(); n = read();
for (i = 1; i <= n; i++) for (j = 1; j <= 3; j++)
orz[i][j] = (now = 1ll * now * A % ZZQ);
for (i = 1; i <= n; i++) sort(orz[i] + 1, orz[i] + 4);
for (i = 1; i <= n; i++) c[i] = cyx(orz[i][1], orz[i][2], orz[i][3]);
sort(c + 1, c + n + 1, comp1);
tot = 0; for (i = 1; i <= n; i++) {
if (i == 1 || c[i].x != c[i - 1].x) tot++;
tm[i] = tot;
}
for (i = 1; i <= n; i++) c[i].x = tm[i];
sort(c + 1, c + n + 1, comp2);
tot = 0; for (i = 1; i <= n; i++) {
if (i == 1 || c[i].y != c[i - 1].y) tot++;
tm[i] = tot;
}
for (i = 1; i <= n; i++) c[i].y = tm[i];
sort(c + 1, c + n + 1, comp3);
tot = 0; for (i = 1; i <= n; i++) {
if (i == 1 || c[i].z != c[i - 1].z) tot++;
tm[i] = tot;
}
for (i = 1; i <= n; i++) c[i].z = tm[i];
for (i = 1; i <= n; i++) a[i] = c[i], a[i].ans = 1;
sort(a + 1, a + n + 1, comp);
czk_is_so_weak(1, n); int ans = 0;
for (i = 1; i <= n; i++) ans = max(ans, a[i].ans);
cout << ans << endl;
return 0;
}