CDQ 分治

CDQ 分治的思想最早由 IOI2008 金牌得主陈丹琦在高中时整理并总结,因此得名。

适用范围

多用于统计有特征的点对 (i,j) 的数量或找出有特征的点对。

流程

对于一个区间 [L,R] 中的点对 (i,j)Li<jR),考虑分为三部分(mid=L+R2):

  • Li<jmidmid+1i<jR:将 [L,R] 拆为 [L,mid],[mid+1,R],发现这两部分点对分别位于这两个区间中,递归处理这两部分。
  • Limid,mid+1jR:设法处理这些点对。

在实现上,通常设一个 solve(L,R) 函数来处理在 [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 分治。

将点按照 ai 排序,对于一个区间 [L,R],假设我们已经处理好了 [L,mid],[mid+1,R] 内的点对,现在考虑处理跨越 mid 的点对。

对于点对 (i,j)imid<j),因为我们已经按照 ai 排序,所以 aiaj 就只要求 ij,因为 i,j 分居 mid 两侧,所以这个限制显然满足。

我们把 [L,mid],[mid+1,R] 区间内的点分别按照 bi 排序。然后对于每一个 j,考虑求出有多少 i 满足 bibj 的限制条件。不难发现,在 j 逐渐向右移时,满足 bibji 的区间右端点是不减的(区间左端点显然为 L),因此我们把 i,j 看作双指针,就可以线性维护。在 i 向右移动时,我们将经过的点的 ci 插入树状数组,对于每一个 j,只要查询有多少 cicj 即可,这样就可以找到合法的点对。

时间复杂度 T(n)=2T(n2)+O(nlogn)=O(nlog2n)

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; 
 }

[NOIP 2023 模拟赛五 By FXT B] 简单第三题

solution & code

NOIP 2023 模拟赛五 题解

posted @   ClapEcho233  阅读(24)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· 写一个简单的SQL生成工具
· Manus的开源复刻OpenManus初探
点击右上角即可分享
微信分享提示