CDQ分治详解

解决问题:

这类问题一般是给你一个长度为 n 的序列,然后让你统计有一些特性的点对(i,j)有多少个,又或者说是找到一对点(i,j)使得一些函数的值最大之类的问题

算法流程:

  1. 找到当前序列中点mid

  2. 将所有的点分为三类

第一种是\(1\leq i\leq mid\ 1\leq j\leq mid\)

第二种是\(1\leq mid+1 \ mid+1\leq j\leq n\)

第三种是\(mid+1\leq i\leq n \ mid+1\leq j\leq n\)

  1. 将(1,n)序列分为两部分(1,mid)和(mid+1,n)

会发现第一类点和第三类点分别被完全包含在两个序列

考虑如何求解第二类点,结合一下题来说明一下吧

经典问题

三维偏序:给定一个序列,每个点有两个性质(\(a_i,b_i\)),求这个序列中有多少个点对满足\(i < j,a_i < a_j,b_i < b_j\)

假设我们现在在solve(l,r),而且现在我们已经求出了solve(l,mid)和solve(mid+1,r)

那么我们现在就是统计\(l\leq i\leq mid,mid+1\leq j\leq r\)的点对(i,j)满足\(i < j,a_i < a_j,b_i < b_j\)

然后我们发现\(i < j\)这个条件没有用了,因为一定满足

剩下两个条件我们可以吧(l,mid)和(mid+1,r)的点分别按照a排序,然后在(mid+1,r)中枚举j,把所有的\(a_i < a_j\)全部插入到数据结构中

因为a已经排好序了,所以对于后一个的j,满足前一个j的\(a_i\)一定满足后一个j

核心代码:

void cdq(int l,int r){
	if (l == r) return;
	int mid = (l+r >> 1);
	cdq(l,mid),cdq(mid+1,r);
	sort(a+l,a+mi+1,cmpy),sort(a+mid+1,a+r+1,cmpy);
	int j = l;
	for (int i = mid+1;i <= r;i++){
		while (a[j].y <= a[i].y&&j <= mid) add(a[j].z,a[j].w),j++;
		a[i].ans += query(a[i].z;)
	}
	for (int i = l;i < j;i++) add(a[i].z,-a[i].w);
}
posted @ 2021-05-08 10:44  小又又yyyy  阅读(79)  评论(0编辑  收藏  举报