BZOJ2253 [2010 Beijing wc]纸箱堆叠

针对一个生产纸箱的工厂在运输过程中如何最大化利用空间的问题,本文介绍了一种高效的算法解决方案。通过采用CDQ分治算法结合树状数组,解决了寻找最多可嵌套堆叠纸箱数量的问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Address

Description

  • P工厂是一个生产纸箱的工厂。纸箱生产线在人工输入三个参数 n,p,a 之后,可自动化生产三边边长如下的 n 个纸箱
    • (a_mod_P,a2_mod_p,a3_mod_P)
    • (a4_mod_p,a5_mod_p,a6_mod_P)
    • ….
    • (a3n2_mod_p,a3n1_mod_p,a3n_mod_p)
    • 在运输这些纸箱时,为了节约空间,必须将它们嵌套堆叠起来。
    • 一个纸箱可以嵌套堆叠进另一个纸箱当且仅当它的最短边、次短边和最长边长度分别严格小于另一个纸箱的最短边、次短边和最长边长度。这里不考虑任何旋转后在对角线方向的嵌套堆叠。
    • 你的任务是找出这n个纸箱中数量最多的一个子集,使得它们两两之间都可嵌套堆叠起来。
    • Input

      • 输入文件的第一行三个整数,分别代表 a,p,n

      Output

      • 输出文件仅包含一个整数,代表数量最多的可嵌套堆叠起来的纸箱的个数。

      Sample Input

      • 10 17 4

      Sample Output

      • 2

      Interpretation

      • 生产出的纸箱的三边长为 (10,15,14),(4,6,9),(5,16,7),(2,3,13) 。其中只有 (4,6,9) 可堆叠进 (5,16,7) ,故答案为2。
      • 2P2000000000,1a<p,ak_mod_p0,ap2000000000,1N50000

      Solution

      30pts

      • 将每个纸箱按照最短边为第一关键字,次短边为第二关键字,最长边为第三关键字从小到大排序。
      • 一个显而易见的 DP,设 f[i] 表示嵌入第 i 个纸箱(排完序后)时最多能嵌套的纸箱个数,则f[i]=max{f[j]+1|j<i,ji}
      • 时间复杂度 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,zk1] 的前缀最大值。
        • 处理完后清空树状数组,还原右区间的元素顺序。
        • 最后递归右区间 [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()
      {   
      //  freopen("box.in", "r", stdin);
      //  freopen("box.out", "w", stdout);
      
          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;
      }
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值