当前位置: 首页 > news >正文

三维偏序 -- cdq 套 cdq

似乎题解区并没有 cdq 套 cdq 的作法,刚好今天讲了,就来写一发。

题意与记号

题目讲的很清楚了。此方法并没有树状数组好想也没有其高效,但能很方便扩展。下文记原序列为 ddd,将每个点拆分成点与询问,内部增加一个名为 ididid 的数据,若其为 −1-11,则是点,否则是询问。

使用类 c++ 数组的书写方式,否则角标太复杂实在不好看。下文先讲二维再讲三维。

二维偏序

先对 xxx 维排序,再分治 yyy 维,每一次分治中点为 midmidmid 的区间 [l,r)[l,r)[l,r),我们都能保证,也必须保证 d[a].x≤d[b].xd[a].x\le d[b].xd[a].xd[b].xa∈[l,mid)a\in[l,mid)a[l,mid)b∈[mid,r)b\in[mid,r)b[mid,r)
L={d[l],d[l+1],d[l+2],… } ⁣ ⁣ ⁣ ⁣ ⁣ ⁣ ⁣ ⁣ ⁣ ⁣ ⁣ ⁣ ⁣ ⁣ ⁣ ⁣ ⁣ ⁣ ⁣ ⁣ ⁣ ⁣ ⁣ ⁣↑R={d[mid],d[mid+1],… } ⁣ ⁣↑ L=\{d[l],d[l+1],d[l+2],\dots \} \\ \!\!\!\!\!\!\!\!\!\!\!\!\!\!\!\!\!\!\!\!\!\!\!\! \uparrow \\ R=\{d[mid],d[mid+1],\dots \} \\ \!\! \uparrow L={d[l],d[l+1],d[l+2],}R={d[mid],d[mid+1],}
我们对两边同时向后处理(并非同步),我们正在处理 d[a]d[a]d[a]d[b]d[b]d[b],其中 d[a].id=−1d[a].id=-1d[a].id=1d[b].id≠−1d[b].id\ne-1d[b].id=1(其它情况不用考虑)。

如果 d[a].y≤d[b].yd[a].y\le d[b].yd[a].yd[b].y,由于分治,LLLRRR 内以 yyy 为关键字肯定是有序的,所以 d[a′].y≤d[b′].yd[a'].y\le d[b'].yd[a].yd[b].ya′∈[l,a]a'\in[l,a]a[l,a]b′∈[b,r]b'\in[b,r]b[b,r]

所以此时我们便可以记录 d[a]d[a]d[a] 的贡献(这里是 111),并将 d[a]d[a]d[a] 扔到归并排序的辅助数组里, a←a+1a \leftarrow a+1aa+1

同理,如果 d[a].y>d[b].yd[a].y > d[b].yd[a].y>d[b].y,我们直接由之前的贡献总和便可以计算答案,并将 d[b]d[b]d[b] 扔到归并排序的辅助数组里,b←b+1b\leftarrow b+1bb+1

三维偏序

进入正题,仿照上面的方法,在第一次 cdq 内部,每一层再执行另一种 cdq(cdq2),如果我们能保证 LLLRRR 内部同时关于 xxxyyy 有序就好了,可是我们在分治的过程中必然会将 xxx 打乱,如果有一种方法,能告诉我们 xxx 曾经在哪边,便能够解决这个问题。

也就是说:我们只想要曾经(xxx 维)在左边点的对右边询问的做贡献。

为此,我们扩展 ddd,加入一个名为 tagtagtag 的数据表示在当层是在左边还是右边,由于此时 yyy 早已有序,仿照二维进行 cdq2。
解释见代码吧,超详细的!

namespace PB {node d[N];                                             // 存储当前处理的节点数组,包含点和查询void solve(int l, int r) {                             // 处理区间 [l, r) 的 z 维偏序if (l == r - 1) return;                            int mid = (l + r) >> 1;                            solve(l, mid), solve(mid, r);                      int a = l, b = mid, c = l, sum = 0;                // sum 记录左边点的数量while (a < mid || b < r) {                         // 归并排序,按 z 坐标合并if (b == r || (a < mid && d[a].z <= d[b].z)) { // 左边还有元素且 z 更小(或右边已空)if (d[a].id == -1 && d[a].tag == LEFT)     // 如果是左边区间的一个点++sum;                                 tp[c++] = d[a++];                          } else {                                       // 右边 z 更小(或左边已空)if (d[b].id != -1 && d[b].tag == RIGHT)    // 如果是右边区间的查询anst[d[b].id] += sum;                  // 累加当前左边点的数量到查询结果tp[c++] = d[b++];                          }}copy(tp + l, tp + r, d + l); // 将临时数组复制回原数组,完成归并}
} // namespace PBnamespace PA {void solve(int l, int r) {                             // 处理区间 [l, r) 的 y 维偏序if (l == r - 1) return;                            int mid = (l + r) >> 1;                            solve(l, mid), solve(mid, r);                      // 递归处理左半区间 [l, mid) 和右半区间 [mid, r)int a = l, b = mid, c = l;                         // a, b 分别指向左右区间,c 指向临时数组while (a < mid || b < r) {                         // 归并排序,按 y 坐标合并if (b == r || (a < mid && d[a].y <= d[b].y)) { // 左边还有元素且 y 更小(或右边已空)d[a].tag = LEFT, tp[c++] = d[a++];         // 标记为左区间} else {                                       // 右边 y 更小(或左边已空)d[b].tag = RIGHT, tp[c++] = d[b++];        // 标记为右区间}}copy(tp + l, tp + r, d + l);     // 将临时数组复制回原数组,完成 y 维归并copy(tp + l, tp + r, PB::d + l); // 将排序后的数组复制到 PB 命名空间的 d 数组PB::solve(l, r);                 // 调用 PB 处理 z 维偏序}
} // namespace PA

时间复杂度

PB 中一次处理长度为 nnn 的区间 O(n)=nlog⁡nO(n)=n\log nO(n)=nlogn

PA(总):O(∑i=0log⁡nT(n2i)×2i)=O(nlog⁡2n)O(\sum_{i=0}^{\log n}T(\frac{n}{2^i})\times 2^i) = O(n\log^2n)O(i=0lognT(2in)×2i)=O(nlog2n)

http://www.dtcms.com/a/318062.html

相关文章:

  • 蓝桥杯----锁存器、LED、蜂鸣器、继电器、Motor
  • 【YOLOv8改进 - C2f融合】C2f融合EBlock(Encoder Block):低光增强编码器块,利用傅里叶信息增强图像的低光条件
  • 分类数据集 - 水稻叶病虫害分类数据集下载
  • Java面试宝典:Java内存模型与对象可达性判定原理
  • Python爬虫实战:研究spiderfoot工具,构建网络情报收集系统
  • java 之 继承
  • jdk动态代理如何实现
  • 【补题】Codeforces Round 779 (Div. 2) C. Shinju and the Lost Permutation
  • 【补题】CodeTON Round 1 (Div. 1 + Div. 2, Rated, Prizes!) D. K-good
  • 大数据之HBase
  • 深度学习-卷积神经网络CNN-多输入输出通道
  • MySQL数据库索引及底层数据结构
  • 宝塔部署go 项目
  • Maven--打包方式详解 (pom、war、jar)
  • 各类排序算法
  • FastAPI(未结束)
  • 【React 插件】@uiw/react-md-editor 使用教程:从基础使用到自定义扩展
  • STM32串口通信指南
  • 基于RPR模型的机械臂手写器simulink建模与仿真
  • easyExcel 读取有合并单元格数据
  • 对接钉钉审批过程记录(C#版本)
  • 高可用改造之构建​​双活冗余的TDengine时序数据处理架构
  • 通过最严时序标准,再登产业图谱榜首,TDengine 时序数据库在可信数据库大会荣获双荣誉
  • AI 软件工程开发 AI 算法 架构与业务
  • Effective C++ 条款25:考虑写出一个不抛异常的swap函数
  • linux 使用docker时开放的端口不受防火墙控制的解决方案
  • 医疗AI中GPU部署的“非对等全节点架构“方案分析(上)
  • AI领域的三箭齐发之夜 - genie3,gpt-oss, Opus 4.1
  • hyper-v常见问题解答(此文会持续更新)
  • DNS 服务器