首页 > 其他分享 >CDQ分治

CDQ分治

时间:2022-11-11 19:47:09浏览次数:74  
标签:ch int 分治 mid ge CDQ 指针

0x00 简介

-- 是的 , \(CDQ\) 分治是一款由女oier \(CDQ\) 引入的分治算法 , 可以利用分治让我们离线地解决一些在线数点问题

-- 你说的对 , 但是面对强制在线的题目 , \(CDQ\)就会寄掉

0x01 引入

三维偏序 ( 陌上花开 )

题目大意 : 对于一个点有三个关键值 : \(a,b,c\) ; 我们定义一个点 \(i\) 比另一点 \(j\) 大 , 当且仅当$a_i\ge a_j 且 b_i\ge b_j 且 c_i\ge c_j $ , 设点 \(i\) 比 \(f(i)\) 个点大 , 求对于每个\(d\) , 有多少 \(f(i)\) 满足 \(f(i)=d\)

0x02 思路

我们的目标就是对满足$a_i\ge a_j 且 b_i\ge b_j 且 c_i\ge c_j $这三个条件的点对进行记数

三个条件没有优先级 , 是平等的

所以我们可以先选择一个条件来满足

就先满足有关 \(a\) 的吧 , 方法很简单 , 就是把点以 \(a\) 为第一关键字进行排序

bool comp(node a,node b) { // 第一次排序 : 对a进行排序
	if(a.a==b.a && a.b==b.b)
		return a.c<b.c;
	if(a.a==b.a)
		return a.b<b.b;
	return a.a<b.a;
}

解决了 \(a\) , 下面处理 \(b\) 和 \(c\)

这时用到了 \(CDQ\) 分治的思想 , 把序列一分为二 , 分别统计

假设我们在处理的序列区间是\([l,r]\) , 把它分成 \([l,mid]\) 和 \([mid+1,r]\)

那么如何把两个区间合并起来计算答案呢 ?

首先 , 由于我们已经对 \(a\) 进行了排序 , 所以只可能是前半段对后半段做贡献

而且 , 前半段点的顺序是不会对贡献产生任何影响的

因此 , 我们再把两段分别以 \(b\) 为关键字进行排序 , 这样可以方便后面的计算

然后就是计算两段的贡献了 , 我们对两段序列都开一个指针 , 开始指向序列的第一个

接下来我们以右边序列的指针为基准 , 往后移动左边指针直到左指针的点的 \(b\) 比右指针指点的 \(b\) 大 , 我们把左边满足 \(b\) 要求的点放入 "待选集合"。由于我们已经对左右边的点按 \(b\) 排序过 , 所以我们右移右指针后 , 前一个数产生的待选集合中的点也都是满足 \(b\) 条件的 , 可以直接继承过来

然后就是判断 \(c\) 条件。假设右指针对应点的 \(c=c_R\) , 待选集合为$ S $ , 我们的目标是算 $\sum_{i\in S}(c_i\le c_r) $ , 如何快速算出这个东西呢 ? 很容易想到可以利用权值树状数组进行维护

最后注意 , 处理完当前区间后要清空树状数组 , 以免对后面产生影响

\(code\) \(of\) \(CDQ\)

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,comp2);//按 b 排序
	sort(s+mid+1,s+r+1,comp2);
	int p1=1,p2=mid+1;// 左右指针
	for(; p2<=r ; p2++){ 
		while(p1<=mid && s[p1].b<=s[p2].b)
			bit.add(s[p1].c,s[p1].cnt),p1++;//把左边的点加入待选集合 , 用树状数组维护
		s[p2].ans+=bit.query(s[p2].c);//统计答案
	}
	for(int i=l;i<p1;i++) // 注意这里是i<p1!因为p1并没有被加入待选集合!
		bit.add(s[i].c,-s[i].cnt);//撤销影响
	return ;
}

在加上一些小东西就可以完成本题了 :

  1. 对于 \(a,b,c\) 完全相等的点 , 我们可以直接把他们压成一个 , 可以节省时间复杂度
  2. 好像没了 , 这条是凑数的

完整的 \(code\) :

#include <bits/stdc++.h>
using namespace std;
inline int read(){
	char ch=getchar();
	int s=0,f=1;
	for(;!isdigit(ch);ch=getchar()) if(ch=='-') f*=-1;
	for(; isdigit(ch);ch=getchar()) s*=10,s+=ch-'0';
	return s*f;
}
inline int lowbit(int x){
	return x&(-x);
}
const int N = 1e5+10;
struct node {
	int a, b, c;
	int cnt;
	int ans;
}s[N];
int n,k;
struct Bit{// 权值树状数组
	int a[N];
	void add(int x,int y) {
		for(;x<=k;x+=lowbit(x))
			a[x]+=y;
	}
	int query(int x){
		int ans=0;
		for(;x;x-=lowbit(x))
			ans+=a[x];
		return ans;
	}
}bit;
int ans[N];
bool comp(node a,node b) { // 第一次排序 : 对a进行排序
	if(a.a==b.a && a.b==b.b)
		return a.c<b.c;
	if(a.a==b.a)
		return a.b<b.b;
	return a.a<b.a;
}
bool comp2(node a,node b){ // 第二次排序 : 对b进行排序
	if(a.b==a.b)
		return a.c<b.c;
	return a.b<b.b;
}
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,comp2);
	sort(s+mid+1,s+r+1,comp2);
	int p1=1,p2=mid+1;
	for(; p2<=r ; p2++){
		while(p1<=mid && s[p1].b<=s[p2].b)
			bit.add(s[p1].c,s[p1].cnt),p1++;
		s[p2].ans+=bit.query(s[p2].c);
	}
	for(int i=l;i<p1;i++)
		bit.add(s[i].c,-s[i].cnt);
	return ;
}
int main(){
	n=read(),k=read();
	for(int i=1;i<=n;i++)
		s[i].a=read(),s[i].b=read(),s[i].c=read();
	sort(a+1,a+n+1,comp);
	int tot=0,m=0;
	for(int i=1;i<=n;i++){// 把相同点压缩
		tot++;
		if(s[i].a!=s[i-1].a || s[i].b!=s[i-1].b || s[i].c!=s[i-1].c){
			s[++m]=s[i];
			s[m].cnt=tot;
			tot=0;
		}
	}
	cdq(1,m);
	for(int i=1;i<=m;i++)
		ans[s[i].ans+s[i].cnt-1] += s[i].cnt;//注意和它完全相等的点也要统计答案 , 而自己不行 , 所以答案就是  s[i].ans+s[i].cnt-1
	for(int i=0;i<n;i++){
		printf("%d\n",ans[i]);
	}
	return 0;
}

标签:ch,int,分治,mid,ge,CDQ,指针
From: https://www.cnblogs.com/sheepcsy/p/16880701.html

相关文章

  • CDQ && 珂朵莉树
    对于题目:P4690[Ynoi2016]镜中的昆虫我们零基础从各个小部分开始学习,并且A了Ta.Part1CDQ分治一看到这个东西,一定会觉得很吓人,觉得是什么高大上的东西。其实不......
  • 线段树(Segment Tree)是一个基于分治的数据结构。
    线段树(SegmentTree)是一个基于分治的数据结构。线段树杂谈 概念:线段树(SegmentTree)是一个基于分治的数据结构。通常处理区间,序列中的查询,更改问题。大体上有单修,单......
  • 【模板】点分治 Centroid Decomposition
    postedon2022-07-2018:59:16|under模板|source0x00模板(P3806)给定\(n,k\)和一棵树,计算\[\sum\limits_{i,j\leqn}[{\ttdist}(i,j)=k]\]即树上距离为\(k\)......
  • API网关第三章-分治处理会话流程
    API网关第三章-分治处理会话流程一、前言这一章所做的就是让职责更加分明,Netty服务端只负责接受网络连接调用Session,Session单独拿出来去处理具体的调用逻辑。二、......
  • 点分治学习笔记
    点分治点分治用于求解树上路径有关的问题。其具体思想,对于当前处理的这一个分治区域,我们计算所有区域内跨过分治中心这一个点的所有路径的贡献,然后将分治中心及与其相邻......
  • 【ZJOI2007】捉迷藏(动态树分治)
    显然只有一次询问的话,可以用点分治来实现。但是现在我们有多组询问,还带有修改,我们只能通过动态点分治来做了。动态点分治的主要思想:省去每次点分治求重心的过程,直接预处......
  • 【XSY4746】会议选址(三度化,边分治,点分治)
    这种关键点的重心问题,一般有两种思路。一种是合并:对于两个不交的点集\(S,T\),\(S\cupT\)的重心必在\(S,T\)重心间的路径上,二分即可,需要数据结构支持dfn区间内\(S\c......
  • 【XSY3979】数据结构(分治,剪枝)
    题面数据结构题解挺神奇的一道题。正解是对\(y\)坐标分治。每次考虑\(y\)坐标在\([l,mid]\)范围内的红点和\(y\)坐标在\([mid+1,r]\)范围内的蓝点匹配成点......
  • 【XSY3972】树与图(树形dp,树剖,分治NTT)
    题面树与图题解不难发现本题可以转化成以下题目:给定一个\(n\)个点的有根树,你可以在树上选择\(k\)个点,满足对于任意两个点都不互为祖先关系,且从根到每个叶子的路......
  • 【XSY3971】不难题(点分治)
    题面不难题题解百年未有之写点分……好久没写了,也当复习了一遍吧。对于树上的一个扫描半径为\(d\)的在\(u\)节点的雷达,我们将其所能覆盖到的点的集合称作“圆\(......