Appearance
cdq 分治是用于解决二维偏序的分治算法(因为 N 维偏序可以通过排序钦定第一维的顺序,所以 N 维偏序等价于求新序列的 N-1 维偏序)。
对于序列
,递归左区间 ,递归右区间 ,遍历左/右区间,使用数据结构维护另一个区间的贡献
点击展开代码
cpp
void cdq(int l, int r) {
if (l >= r) {
return;
}
int mid = l + r >> 1;
cdq(l, mid);
cdq(mid + 1, r);
int now = l;
for (int i = mid + 1; i <= r; i++) {
while (now <= mid && a[now].a <= a[i].a) {
// 第一维 已升序,维护 第二维 的贡献
now++;
}
// 计算 i 的答案
}
for (int i = l; i < now; i++){
// 删除 [l, now] 的贡献
}
// 归并 第一维 升序
int cnt = 0;
now = mid + 1;
for (int i = l; i <= mid; i++) {
while (now <= r && a[now].a <= a[i].a) {
A[++cnt] = a[now];
now++;
}
A[++cnt] = a[i];
}
while (now <= r) {
A[++cnt] = a[now];
now++;
}
for (int i = 1; i <= cnt; i++)
a[l - 1 + i] = A[i];
}时间复杂度
复杂度应按具体维数和每一层实际执行的操作分别计算。一次 CDQ 分治有
例如,三维偏序通常先按第一维排序,用 CDQ 分治处理第二维的先后关系,并用树状数组维护第三维。CDQ 每层共处理
实际竞赛中还应结合常数判断实现是否可行:维数升高后,多层分治和数据结构操作的常数较大,理论上优于
重复点与等号
处理偏序前必须先明确各维使用严格小于还是小于等于。对于各维均采用“cnt,修改数据结构时加入 cnt。设从其他点组得到的贡献为 ans,若答案不计自身,则该组中每个原点的答案为 ans + cnt - 1;若计入自身,则为 ans + cnt。
若某一维要求严格小于,或相等坐标之间不应产生贡献,则同坐标事件需要成组处理:先完成这一组的查询,再统一执行修改,避免组内元素相互贡献。排序规则、归并时使用的 < 或 <=,以及数据结构查询的边界必须采用同一套等号约定。
