CDQ分治学习笔记

1. 简介

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

  • 解决与点对相关的问题

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

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

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

2.1. 流程

1.找到序列的中点mid

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

a. imid,jmid

b. imid,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 个元素有 ai,bi,ci 三个属性,设 f(i) 表示满足 ajaibjbicjcijij 的数量。

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

输入格式

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

接下来 n 行,每行三个整数 ai,bi,ci,分别表示三个属性值。

输出格式

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

提示

1n1051ai,bi,cik2×105

2.3. 解法

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

首先将三元组按a排序

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

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

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

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

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

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 @   wangsiqi2010916  阅读(20)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律
点击右上角即可分享
微信分享提示