首页 > 其他分享 >cdq 分治

cdq 分治

时间:2024-12-09 21:54:22浏览次数:5  
标签:偏序 int 分治 mid cdq 区间

简介

cdq 分治常用于计算序列中需要满足某些限制的点对对答案的贡献,通常点对有 \(O(n^2)\) 个。

核心思想

与普通分治类似,把点对分成前半个区间和后半个区间的点对,但 cdq 分治还要处理跨越区间中点的点对,这就是 cdq 分治的核心所在。

算法流程

下面以三维偏序为例。 P3810 【模板】三维偏序(陌上花开)

首先把元素相同的合并,因为 cdq 分治一般解决不了有重复元素的问题,除非重复元素之间不算贡献

与二维偏序类似,我们把所有元素按 \(a_i,b_i,c_i\) 分别为第一、二、三关键字排序,可以去除一维,变成带顺序限制的二维偏序问题。

此时右区间的每一个点显然都不会对左区间的点产生贡献,那么对左右区间分别按 \(a_i,b_i,c_i\) 为第一、第二关键字排序。观察到此时对于右区间的每一个 \(i\),符合限制 \(b_j \leq b_i\) 的 \(j\) 一定是左区间的一段随着 \(i\) 增大单调不降的前缀。对于这个前缀查询,用一个 BIT 维护即可。

视值域与序列长度同阶,复杂度为 \(O(n \log^2 n)\).

注意重复元素也有贡献。

struct node {
	int a,b,c,cnt,ans,id;
}G[N];

BIT T; 

void solve(int l,int r) {
	if(l>=r) return;
	int mid=(l+r)>>1,le=l;
	solve(l,mid);solve(mid+1,r); // 分治计算不跨越中点的点对贡献 
	std::sort(e+l,e+mid+1,[](node a,node b) {return a.b==b.b?a.c<b.c:a.b<b.b;}); // 注意排序的地址位置 
	std::sort(e+mid+1,e+r+1,[](node a,node b) {return a.b==b.b?a.c<b.c:a.b<b.b;});
	for(int i=mid+1;i<=r;i++) { // 双指针统计答案 其中 i 指向右区间 le 指向左区间 
		while(le<=mid && e[le].b<=e[i].b) {T.modify(e[le].c,e[le].cnt);le++;}
		e[i].ans+=T.query(e[i].c);
	}
	for(int i=l;i<le;i++) T.modify(e[i].c,-e[i].cnt); // 清空 BIT 
}

int main() {
	// do something... 
	std::sort(G+1,G+1+n,[](node a,node b) {return a.a==b.a?(a.b==b.b?a.c<b.c:a.b<b.b):a.a<b.a;});
	for(int i=1;i<=n;i++) { // 合并相同元素 
		if(G[i].a!=G[i-1].a || G[i].b!=G[i-1].b || G[i].c!=G[i-1].c) e[++m]=G[i];
		++e[m].cnt;e[m].id=m;
	}
	solve(1,m);
	// do something... 
	return 0;
} 

注意每次分治/排序的时候对边界的计算。

结语

排序的地址有被恶心到一开始调了好久,以后注意。

感觉对分治的理解又加深了,其实以前一直不怎么懂分治。

参考文章

[1] 分治专题 https://www.cnblogs.com/alex-wei/p/divide_and_conquer.html

标签:偏序,int,分治,mid,cdq,区间
From: https://www.cnblogs.com/ldh081122/p/18596096

相关文章

  • CDQ分治
    算法简介用于解决三维数点问题:给定形如你个\((x,y,z)\)的三维坐标,然后让你求有多少个点三个维度的坐标都小于这个点做法:用分治思想将其转化为二维数点二位数点:先用排序解决掉第一维,再用树状数组维护第二维小于它的点数分治思想:我们把一个区间划分为两半,我们只统计左半部......
  • 【学习笔记】树分治
    点分治普通的分治在一段子段\([l,r]\)中处理和\(mid\)有关的信息然后递归处理\([l,mid)\)和\((mid,r]\)。由于中点的优秀性质这种看似暴力的做法实际复杂度是\(O(n\logn)\)的。点分治是一种把分治思想运用到树上解决问题的算法(但是其实更多人愿意称其为数据结构?)。它一......
  • 【题解】P5787 二分图 /【模板】线段树分治
    二分图最简单的方法是染色法实现,但是扩展域并查集也可以实现,有两个集合\(S,T\),具体的是相连边的两个点\(x,y\)总是在不同的两个集合中,若出现在同一集合中即不是一个二分图。对于时间段建边考虑用线段树储存,线段树按照时间轴划分,将将对应时间区间的节点储存上当前连边操作,小时......
  • 递归和分治
    一.递归的概念一个递归过程表示为基语句和终止条件。递归算法的思想是将较大规模的对象的操作归结为对较小规模的对象实施同样的操作。将复杂问题分解为较为简单的子问题组合。举例:单链表是一种递归的数据结构,hanoi问题是一种递归的问题求解。优点:结构清晰可读性强,容易用数......
  • 归并分治
    [Algo]归并分治1.经典归并排序//1.经典归并排序voidmerge(vector<int>&v,intleft,intmid,intright){vector<int>tmp(v);inti=left,j=mid+1,k=left;while(i<=mid&&j<=right)tmp[k++]=v[i]<=v[j]?v[......
  • 递归、分治和动态规划
    递归、分治和动态规划是算法中的三种重要思想,尽管它们有一些相似之处,但在具体实现和应用上有所不同。下面我将逐一讲解这三者的概念和区别。1.递归(Recursion)递归是算法中的一种思想,指的是通过将一个大问题分解为规模较小的相同问题来求解问题。递归通过函数自己调用自己来实现......
  • 蓝桥杯分治
    P1226【模板】快速幂题目描述给你三个整数 ......
  • LeetCode刷题 -- 分治快排
    目录颜色分类题目解析算法原理代码排序数组题目解析算法原理代码数组中第K个最大元素题目解析算法原理代码LCR159.库存管理III题目解析算法原理代码颜色分类题目链接题目解析数组分为三块算法原理1.如果nums[i]==0,++left,i++下标对应元素交换,先+......
  • 算法时间复杂度和空间复杂度计算方法(O(1)、O(n)、O(logn)、O(n^2)、O(n^3 )、O(n!))分
    文章目录时间复杂度与空间复杂度计算方法一、时间复杂度概述1.1时间复杂度的定义1.2常见的时间复杂度-**常数复杂度O(......
  • 12-15分治法的应用
    分治法的应用前提条件如图:13.二分搜索#include<iostream>usingnamespacestd;constintN=1e6;intn,m;intq[N];//对于二分分界来说左加右减//对于取中值来说,男左女右,男是1,不用+,女需要+1intmain(){cout<<"请输入数组个数以及查寻的数的个数"<<endl;cin>>......