首页 > 其他分享 >CDQ分治

CDQ分治

时间:2024-04-24 19:45:47浏览次数:11  
标签:node cnt int 分治 mid CDQ data

CDQ分治

它是一种非常巧妙地方法。

用分治的思想处理三维偏序一类的问题:

处理的问题如下:模板

有 $ 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 $ 的数量。

也就是对于每一个三元组 \(i\) 看有多少个完全小于他的三元组 \(j\)。

数据范围 \(n \le 2\times 10^5\)。

显然暴力会不过去。

只能考虑一些 \(n \log n\) 或 \(n \log^2 n\) 的做法。

首先,先把第一维排序了。

这样,只用考虑 \(b_i\) 与 \(c_i\) 了。

那我们考虑如何处理 \(b_i\)。

类似归并的方法,分而治之,分治。就是对于 \([l,r]\) 的子区间我们先处理 \([l,mid]\) 和 \([mid+1,r]\) 两个区间,然后将其 \(O(r-l)\) 的时间复杂度合并。很简单的策略,分别在两个子区间内放两个指针,那边小就加入那边,因为如果 \(A_i\) 他是比 \(B_j\) 小的,那么 \(A\) 目前也排序了,所以也是 \(A\) 中最小的,也就是 \(A\) 和 \(B\) 合并后最小的,因此直接判断然后去加即可。

这样,\(b_i\) 也处理好了,我们就可以用树状数组维护一下 \(c_i\) 的数量,然后就可以求出此时比 \(c_i\) 小的 \(c_j\) 有多少个,由于在线处理,所以说,之前出现的 \(a_i\) 和 \(b_i\) 都是应该符合小于现在的这个三元组的。

于是,在线(边跑边记录)处理完之后,再依次输出答案。

int n,k,m;
struct node{int a,b,c;};
struct data{node a;int cnt,ans;};
node a[N];
struct data b[N];
int t[N<<1];
void add(int x,int val){while(x<=k)t[x]+=val,x+=(x&-x);}
int query(int x){int res=0;while(x)res+=t[x],x-=(x&-x);return res;}
bool node_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;
}bool data_cmp(struct data a,struct data b){
	if(a.a.b != b.a.b) return a.a.b<b.a.b;
	return a.a.c<b.a.c;
}void cdq(int l,int r){
	if(l==r)return ;
	int mid=(l+r)>>1,x=l;
	cdq(l,mid),cdq(mid+1,r);
	sort(b+l,b+1+mid,data_cmp),sort(b+1+mid,b+1+r,data_cmp);
	For(i,mid+1,r){
		while(x<=mid&&b[x].a.b<=b[i].a.b) add(b[x].a.c,b[x].cnt),x++;
		b[i].ans+=query(b[i].a.c);
	}For(i,l,x-1){
		add(b[i].a.c,-b[i].cnt);//清空还原操作
	}
}int ans[N];
void solve(){
	cin>>n>>k;
	For(i,1,n)cin>>a[i].a>>a[i].b>>a[i].c;
	sort(a+1,a+1+n,node_cmp);
	For(i,1,n){
		if(a[i].a!=a[i-1].a||a[i].b!=a[i-1].b||a[i].c!=a[i-1].c) b[++m].a=a[i];
		b[m].cnt++;	
	}
	cdq(1,m);
	For(i,1,m)ans[b[i].ans+b[i].cnt]+=b[i].cnt;
	For(i,1,n)cout<<ans[i]<<endl;
}

标签:node,cnt,int,分治,mid,CDQ,data
From: https://www.cnblogs.com/gsczl71/p/18156173

相关文章

  • CDQ分治学习笔记
    1.简介CDQ分治是一种思想,类似于动态规划,依照原理和写法的不同,大致分为3类:解决与点对相关的问题1D动态规划的优化及转移通过CDQ分治,将一些动态问题转化为静态问题2.解决与点对相关的问题2.1.流程1.找到序列的中点mid2.将所有的点对(i,j)划分为三类a.\(i\lemi......
  • 点分治小记
    点分治是一类高效统计树上路径问题的算法,通过优化递归深度的方法来有效保证时间复杂度。具体操作一般是以下几步:找到当前子树的重心以重心为根计算经过根节点的路径对答案的贡献将根删去并递归处理它的所有子树因为我们每次都以树的重心来作为根节点,递归深度不会超过......
  • P7431【THUPC2017】小 L 的计算题 (牛顿恒等式)(分治NTT)(多项式求逆)题解
    知识点涉及:牛顿恒等式,分治\(NTT\),多项式求逆。这道题有一个推式子之后分治\(NTT+Ln+Exp\)的做法,不过也有一个不用\(Ln+Exp\)的做法(理论常数要小点,实际差不多)。题解:这道题可以牛顿恒等式直接推出一个非常好写的东西。首先看一下牛顿恒等式的描述:对于\(n\)次多项式\(A(......
  • 点分治
    1概念点分治是树分治的一种,主要用于处理树上路径问题。注意这颗树不需要有根,即这是一颗无根树。下面以例题分析点分治的基本思想。2基本思想首先你需要会的前置知识:树的重心。我们来看这样一道例题:【模板】点分治:给出一颗无根树,有边权,询问树上是否存在距离为\(k\)的......
  • 点分树(动态点分治)学习笔记
    1.定义在点分治的基础上加以变化,构造一颗支持快速修改的重构树,称之为点分树2.算法2.1.思路点分治的核心在于通过树的重心来划分联通块,减少合并层数,从而降低时间复杂度所以,我们可以按分治递归的顺序提出一颗树,易知树高至多为logn具体的说,对于每一个找到的重心,将上一次分治......
  • 点分治学习笔记
    前置知识:树的重心对于一颗无根树上的每一个点,计算其所有子树中最大的子节点数,这个值最小的点就是树的重心1.定义点分治,又叫树分治,点分治是一种在树上进行路径静态统计的算法,所谓树上的静态统计,并不是像树剖一样维护路径最值,路径之和一类的统计,点分治的本质其实是将一棵树拆分......
  • #莫队二次离线,根号分治#洛谷 5398 [Ynoi2018] GOSICK
    题目\(m\)组询问求\(\sum_{l\leqi,j\leqr}[a_i\bmoda_j==0],n,m,a_i\leq5\times10^5\)分析设\(f(l,r,x)\)表示\(i或j\in[1,x],i或j\in[l,r]\)时的答案,\(g_x\)表示\([1,x]\)的答案,根号的做法可以通过三秒由于涉及区间内的求值,需要在莫队的基础上二次离线,那......
  • [DS 小计] 线段树分治
    很简单的一个小trick。就看能不能想出来。思考我们学过CDQ分治。CDQ分治的思想是把时间切割,询问截断点后面的问题,预处理截断点前面的贡献。这是把问题和修改放在一起的。那么,假设修改很难撤销怎么办?这时候就可以用线段树分治做到只增不删。有点类似回滚莫队。算法直......
  • 分治思想排序(快速排序、归并排序)
    分治:分而治之,即把一个复杂的问题分成两个或更多的相同或相似的子问题,再把子问题分成更小的子问题……直到最后子问题可以简单的直接求解,原问题的解即子问题的解的合并优点:降低时间复杂度:分治法可以将大问题分解为小问题,通过解决小问题来有效解决原问题,从而降低算法的时间复杂......
  • 算法分析与设计——实验1: 递归与分治
    实验一 递归与分治一、实验目的        1、理解分治算法的概念和基本要素;        2、理解递归的概念;        3、掌握设计有效算法的分治策略。二、实验内容和要求实验要求:通过上机实验进行算法实现,保存和打印出程序的运行结果,并结合程序进行......