CDQ 分治
CDQ 分治的思想最早由 IOI2008 金牌得主陈丹琦在高中时整理并总结,因此得名。
适用范围
多用于统计有特征的点对 的数量或找出有特征的点对。
流程
对于一个区间 中的点对 (),考虑分为三部分():
- ,:将 拆为 ,发现这两部分点对分别位于这两个区间中,递归处理这两部分。
- :设法处理这些点对。
在实现上,通常设一个 solve(L,R)
函数来处理在 区间内的点对:
void solve (int L, int R) {
int mid = (L + R) >> 1; if (L == R) return ; // 边界
solve (L, mid); solve (mid + 1, R); // 递归处理更小的区间
... // 处理跨越 mid 的点对
}
例题
三维偏序
solution
考虑使用 CDQ 分治。
将点按照 排序,对于一个区间 ,假设我们已经处理好了 内的点对,现在考虑处理跨越 的点对。
对于点对 (),因为我们已经按照 排序,所以 就只要求 ,因为 分居 两侧,所以这个限制显然满足。
我们把 区间内的点分别按照 排序。然后对于每一个 ,考虑求出有多少 满足 的限制条件。不难发现,在 逐渐向右移时,满足 的 的区间右端点是不减的(区间左端点显然为 ),因此我们把 看作双指针,就可以线性维护。在 向右移动时,我们将经过的点的 插入树状数组,对于每一个 ,只要查询有多少 即可,这样就可以找到合法的点对。
时间复杂度
code
CI N = 1e5; struct node {int a; int b; int c;} p[N + 5], m[N + 5]; struct llsAKIOI {int b; int c; int id;} q[N + 5]; int n, K, BIT[N * 2 + 5], ans[N + 5], Tng[N + 5], dis[N + 5];
int lowbit (int x) {return x & -x;} void add (int id, int x) {for (; id <= K; id += lowbit (id)) BIT[id] += x;}
int query (int id) {int s = 0; for (; id; id -= lowbit (id)) s += BIT[id]; return s;}
bool cmp (node x, node y) {if (x.a != y.a) return x.a < y.a; if (x.b != y.b) return x.b < y.b; return x.c < y.c;}
bool cmp2 (llsAKIOI x, llsAKIOI y) {return x.b == y.b ? x.c < y.c : x.b < y.b;}
void solve (int L, int R) {
RI i, j; if (L == R) return ; int mid = (L + R) >> 1; solve (L, mid); solve (mid + 1, R); for (i = L; i <= R; ++ i) q[i].b = p[i].b, q[i].c = p[i].c, q[i].id = i;
sort (q + L, q + mid + 1, cmp2); sort (q + mid + 1, q + R + 1, cmp2); i = L; for (j = mid + 1; j <= R; ++ j) {
W (i <= mid && q[i].b <= q[j].b) add (q[i].c, dis[q[i].id]), ++ i; ans[q[j].id] += query (q[j].c);
} W (i > L) -- i, add (q[i].c, - dis[q[i].id]);
}
int main () {
RI i, j; for (Read (n, K), i = 1; i <= n; ++ i) Read (m[i].a, m[i].b, m[i].c); sort (m + 1, m + n + 1, cmp);
int cnt = 0, tme = 0; for (i = 1; i <= n; ++ i) {
++ tme; if (m[i].a != m[i + 1].a || m[i].b != m[i + 1].b || m[i].c != m[i + 1].c) {
++ cnt; p[cnt] = m[i]; dis[cnt] = tme; tme = 0;
}
} solve (1, cnt);
for (i = 1; i <= cnt; ++ i) Tng[ans[i] + dis[i] - 1] += dis[i]; for (i = 0; i < n; ++ i) printf ("%d\n", Tng[i]); printf ("\n");
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· 写一个简单的SQL生成工具
· Manus的开源复刻OpenManus初探