CDQ分治学习笔记

1. 简介

CDQ分治是一种思想,类似于动态规划,依照原理和写法的不同,大致分为3类:

  • 解决与点对相关的问题

  • 1D动态规划的优化及转移

  • 通过CDQ分治,将一些动态问题转化为静态问题

2. 解决与点对相关的问题

2.1. 流程

1.找到序列的中点mid

2.将所有的点对(i,j)划分为三类

a. \(i\le mid,j\le mid\)

b. \(i\le mid,j>mid\)

c. \(i>mid,j>mid\)

3.将原序列拆成(1,mid),(mid+1,n)两个序列,第一和第三类可以递归处理,第二类则另外想办法

2.2. 例题

【模板】三维偏序(陌上花开)

oj:https://gxyzoj.com/d/gxyznoi/p/P18

题目描述

有 $ n $ 个元素,第 $ i $ 个元素有 $ a_i,b_i,c_i $ 三个属性,设 $ f(i) $ 表示满足 $ a_j \leq a_i $ 且 $ b_j \leq b_i $ 且 $ c_j \leq c_i $ 且 $ j \ne i $ 的 \(j\) 的数量。

对于 $ d \in [0, n) $,求 $ f(i) = d $ 的数量。

输入格式

第一行两个整数 $ n,k $,表示元素数量和最大属性值。

接下来 $ n $ 行,每行三个整数 $ a_i ,b_i,c_i $,分别表示三个属性值。

输出格式

$ n $ 行,第 $ d + 1 $ 行表示 $ f(i) = d $ 的 $ i $ 的数量。

提示

$ 1 \leq n \leq 10^5$,$1 \leq a_i, b_i, c_i \le k \leq 2 \times 10^5 $。

2.3. 解法

这是一道和点对有关的问题

首先将三元组按a排序

假设此时已经写好了solve(l,r),且通过求解得到了solve(l,mid),solve(mid+1,r),此时,可以考虑如何求解第二部分的答案

可以发现,此时因为已经排序,所以\(a_i\le a_j\)的条件已经没什么用了,那么此时只剩下两个条件,可以枚举j来确定有多少个i满足条件

为方便枚举,所以可以将两侧的值按b分别排序,这里可以枚举j,将满足条件的i插入某种数据结构中,例如树状数组,然后查询有多少个c满足条件,便是j的答案

当我们插入一个c,其值为x时,就令x单点+1,而满足条件的点数就是前缀和,在操作前离散化即可

对于每一个j,我们需要将所有\(b_i\le b_j\)加入树状数组,由于已经排好了序,所以双指针插入即可

2.4. 代码

#include<cstdio>
#include<algorithm>
using namespace std;
int n,m,maxval;
struct node{
	int a,b,c,cnt,res;
}t[100005],s[100005];
bool cmp(node a,node b)
{
	if(a.a!=b.a) return a.a<b.a;
	if(a.b!=b.b) return a.b<b.b;
	return a.c<b.c;
}
int lowbit(int x)
{
	return x & (-x);
}
int sum[200005];
void add(int id,int val)
{
	while(id<=maxval)
	{
		sum[id]+=val;
		id+=lowbit(id);
	}
}
int query(int id)
{
	int ans=0;
	while(id)
	{
		ans+=sum[id];
		id-=lowbit(id);
	}
	return ans;
}
bool cmp2(node a,node b)
{
	if(a.b!=b.b) return a.b<b.b;
	return a.c<b.c;
}
void CDQ(int l,int r)
{
	if(l==r) return;
	int mid=(l+r)>>1;
	CDQ(l,mid);
	CDQ(mid+1,r);
	sort(s+l,s+mid+1,cmp2);
	sort(s+mid+1,s+r+1,cmp2);
	int i=l,j=mid+1;
	while(j<=r)
	{
		while(i<=mid&&s[i].b<=s[j].b)
		{
			add(s[i].c,s[i].cnt);
			i++;
		}
		s[j].res+=query(s[j].c);
		j++;
	}
	for(int k=l;k<i;k++)
	{
		add(s[k].c,-s[k].cnt);
	}
}
int res[100005];
int main()
{
	scanf("%d%d",&m,&maxval);
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d%d",&t[i].a,&t[i].b,&t[i].c);
	}
	sort(t+1,t+m+1,cmp);
	int tmp=0;
	for(int i=1;i<=m;i++)
	{
		tmp++;
		if(t[i].a!=t[i+1].a||t[i].b!=t[i+1].b||t[i].c!=t[i+1].c)
		{
			n++;
			s[n].a=t[i].a,s[n].b=t[i].b,s[n].c=t[i].c,s[n].cnt=tmp;
			tmp=0;
		}
	}
	CDQ(1,n);
	for(int i=1;i<=n;i++)
	{
		res[s[i].res+s[i].cnt-1]+=s[i].cnt;
	}
	for(int i=0;i<m;i++)
	{
		printf("%d\n",res[i]);
	}
	return 0;
}
posted @ 2024-04-24 16:40  wangsiqi2010916  阅读(7)  评论(0编辑  收藏  举报