Output
- 输出文件仅包含一个整数,代表数量最多的可嵌套堆叠起来的纸箱的个数。
Sample Output
Interpretation
- 生产出的纸箱的三边长为
(10,15,14),(4,6,9),(5,16,7),(2,3,13)
。其中只有
(4,6,9)
可堆叠进
(5,16,7)
,故答案为2。
-
2≤P≤2000000000,1≤a<p,ak_mod_p≠0,ap≤2000000000,1≤N≤50000
Solution
30pts
- 将每个纸箱按照最短边为第一关键字,次短边为第二关键字,最长边为第三关键字从小到大排序。
- 一个显而易见的 DP,设
f[i]
表示嵌入第
i
个纸箱(排完序后)时最多能嵌套的纸箱个数,则f[i]=max{f[j]+1|j<i,j能嵌套进i}。
- 时间复杂度
O(n2)
,期望得分30分。
100pts
- 为表示方便,记最短边、次短边、最长边分别为
x,y,z
。
- 考虑把问题转化:
- 用一个数据结构来维护
f[i]
。
- 对于箱子
i
,先从数据结构中查询可以嵌套的箱子中最大的f[j]转移。
- 然后再把转移得到的
f[i]
加入数据结构。
- 这里要求箱子j的
x,y,z
都比箱子
i
的小,正常的数据结构显然都要至少开到二维,空间无法承受,而树套树的代码量和常数也并非优秀,这时候我们就想到了CDQ分治。
- CDQ分治适用于满足以下两个条件的数据结构题:
- 修改操作对询问的贡献独立,修改操作之间互不相影响。
- 题目允许使用离线算法。
- 我们所要做的查询和修改操作显然满足这两个条件。
- 因此我们将第一维x用排序处理,第二维
y
用分治处理,第三维z用树状数组维护。
- 首先同
30pts
一样按
x,y,z
三关键字排序。
- 然后定义一个函数进行分治:
-
f[i]
显然是从左往右转移的,先递归左区间
[l,mid]
。
- 然后按照CDQ分治的流程,处理左区间对右区间的影响。
- 为了方便处理,这里要忽略掉第一维
x
的影响,因此我们找到的分界点mid要满足
xmid<xmid+1
。
- 这时如果不考虑后两维,左区间的元素都可以转移到右区间来。
- 那么我们将左右区间分别按
y
为第一关键字,z为第二关键字从小到大排序,按顺序处理右区间的每一个元素
k
,每次把左区间中yj<yk的元素的
f[j]
加入以
z
为下标的树状数组,那么转移到f[k]的最优值就是
[1,zk−1]
的前缀最大值。
- 处理完后清空树状数组,还原右区间的元素顺序。
- 最后递归右区间
[mid+1,r]
。
- 这个函数最多递归
logn
层,每层处理的复杂度为
O(nlogn)
,总时间复杂度为
O(nlog2n)
,期望得分100分。
Code
#include <iostream>
#include <cstdio>
#include <cctype>
#include <algorithm>
#include <cstring>
using namespace std;
typedef long long ll;
const int N = 5e4 + 5, M = N * 3;
const int Maxn = 0x3f3f3f3f;
int A, P, n, m, Ans;
int b[M], bit[M], c[M];
struct point
{
int x, y, z, ans;
inline bool operator < (const point &a) const
{
if (y != a.y) return y < a.y;
return z < a.z;
}
}a[N];
inline bool cmp(const point &a, const point &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 void CkMax(int &x, int y) {if (x < y) x = y;}
inline void Modify(int x, int y)
{
for (; x <= m; x += bit[x])
CkMax(c[x], y);
}
inline int Query(int x)
{
int res = 0;
for (; x; x ^= bit[x])
CkMax(res, c[x]);
return res;
}
inline void Clear(int x)
{
for (; x <= m; x += bit[x])
if (c[x] > 0) c[x] = 0;
else break;
}
inline void CDQsolve(int l, int r)
{
if (l >= r) return ;
int mid = l + r >> 1, tl = mid, tr = mid + 1;
mid = Maxn;
while (tl >= l || tr <= r)
{
if (tl >= l && a[tl].x != a[tl + 1].x)
{
mid = tl; break;
}
if (tr <= r && a[tr].x != a[tr - 1].x)
{
mid = tr - 1; break;
}
--tl; ++tr;
}
if (mid == Maxn) return ;
CDQsolve(l, mid);
int j = l, k = mid + 1;
sort(a + l, a + mid + 1);
sort(a + mid + 1, a + r + 1);
for (int i = l; i <= r; ++i)
if (k > r || j <= mid && a[j].y < a[k].y)
Modify(a[j].z, a[j].ans), ++j;
else
CkMax(a[k].ans, Query(a[k].z - 1) + 1), ++k;
for (int i = l; i <= mid; ++i)
Clear(a[i].z);
sort(a + mid + 1, a + r + 1, cmp);
CDQsolve(mid + 1, r);
}
int main()
{
scanf("%d%d%d", &A, &P, &n); int ex = 1;
for (int i = 1; i <= n; ++i)
{
a[i].x = b[++m] = ex = (ll)ex * A % P;
a[i].y = b[++m] = ex = (ll)ex * A % P;
a[i].z = b[++m] = ex = (ll)ex * A % P;
if (a[i].x > a[i].y) swap(a[i].x, a[i].y);
if (a[i].x > a[i].z) swap(a[i].x, a[i].z);
if (a[i].y > a[i].z) swap(a[i].y, a[i].z);
a[i].ans = 1;
}
sort(b + 1, b + m + 1);
m = unique(b + 1, b + m + 1) - b - 1;
for (int i = 1; i <= n; ++i)
{
a[i].x = lower_bound(b + 1, b + m + 1, a[i].x) - b + 1;
a[i].y = lower_bound(b + 1, b + m + 1, a[i].y) - b + 1;
a[i].z = lower_bound(b + 1, b + m + 1, a[i].z) - b + 1;
}
++m;
for (int i = 1; i <= m; ++i) bit[i] = i & -i;
sort(a + 1, a + n + 1, cmp);
CDQsolve(1, n);
for (int i = 1; i <= n; ++i)
CkMax(Ans, a[i].ans);
printf("%d\n", Ans);
return 0;
}