[BZOJ2253][2010 Beijing wc]纸箱堆叠(CDQ分治优化DP)

时间:2022-09-07 14:13:44

考虑一个朴素的DP方案:
先求出所有纸箱的最短边(以下第 i 个纸箱的最短边记为 xi ),次短边(以下第 i 个纸箱次短边记为 yi )和最长边(以下记为 zi ),然后按照 xi 从小到大排序。
这样,就变成了一个求最长严格上升子序列的问题,即 f[i] 为到了第 i 个纸箱,且必须选第 i 个纸箱的最优解:
f[i]=1+maxxj<xi,yj<yi,zj<zif[j]
但这样显然是 O(n2) 的,TLE。考虑优化。
考虑一般的LIS优化:一般的LIS,可以将序列离散化之后,用树状数组进行优化。但这里有 y z 两个关键字,不能简单地用树状数组优化。
还是先离散化序列,设有一个平面,把每一个决策都看作一个点 (yi,zi) ,权值为 f[i] 。那么在不考虑 xi 相等的情况下,
maxxj<xi,yj<yi,zj<zif[j] 就是求点 (yi1,zi1) 的左下角的点的最大权值。决策结束之后,还需要把点 (yi,zi) 插入平面。因此,考虑CDQ分治,就能得到 O(nlog2n) 的算法。但这与普通的CDQ分治有两点区别:
1、将 [l,r] 操作区间分成两个小的操作区间时,有时不能取中点作为分界点。假设分界点为 mid ,分成了 [l,mid] [mid+1,r] 两个小操作区间,那么绝对不能有 xmid=xmid+1 ,否则有可能使 f[i] 从满足 xj=xi j 转移过来。
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;
}