您的位置:首页 > 其它

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

2018-01-06 19:43 344 查看
考虑一个朴素的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]就是求点(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转移过来。

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
[4], tot, T
, tm
, to
;
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
, c
;
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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: