首页 > 其他分享 >CDQ分治

CDQ分治

时间:2024-07-17 17:33:49浏览次数:10  
标签:ch int 分治 mid le CDQ

该分治算法由CDQ提出,主要用于解决三维偏序问题。

下面的内容就三维偏序例题来讲。

题目

给你一个序列,每个元素有 \(a,b,c\) 三个属性,问满足 \(a_i>a_j,b_i>b_j,c_i>c_j\) 的数对 \(i,j\) 的数量。

分析

将原序列按照 \(a\) 值排序,将其变为下标。

CDQ分治的主要步骤是对于一个需要解决的区间 \(l,r\),找到区间中点 \(mid\),并把原区间中的数对 \((i,j)\) 分为三部分
\(i\le mid,j\le mid\)
\(mid\le i,mid\le j\)
\(i\le mid,mid\le j\)

对于前两种数对,递归计算,所以我们需要设计算法解决第三类数对。

考虑把区间 \(l\sim mid\) 和 \(mid\sim r\) 按照 \(b\) 排序,并设计两个指针 \(i,j\),分别从 \(l\) 和 \(mid+1\) 开始向右移动。对于每一个当前的 \(j\),我们需要计算 \(b_i\le b_j\) 的数对个数。因为序列已经排好序,所以我们将 \(i\) 指针一直右移,并把途中的所有 \(c_i\) 加入值域树状数组,最后累加答案并消除影响即可。

Code

#include<bits/stdc++.h>
#define int long long
using namespace std;
inline int read()
{
	int w=1,s=0;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')w=-1;ch=getchar();}
	while(isdigit(ch)){s=s*10+(ch-'0');ch=getchar();}
	return w*s;
}
const int mod=998244353;
const int maxn=2e5+10;
int t[maxn<<2];
int lb(int x){return x&-x;}
void add(int x,int y){for(;x<=maxn;x+=lb(x))t[x]+=y;}
int ask(int x){int ans=0;for(;x;x-=lb(x))ans+=t[x];return ans;}
int n;
struct no
{
	int a,b,c,d,ans;
	inline friend bool operator < (no x,no y)
	{
		return x.a<y.a||x.a==y.a&&x.b<y.b||x.a==y.a&&x.b==y.b&&x.c<y.c;
	}
	inline friend bool operator != (no x,no y)
	{
		return x.a!=y.a||x.b!=y.b||x.c!=y.c;
	}
}x[maxn],y[maxn];
int tot;
void solve(int l,int r)
{
	if(l==r)return ;
	int mid=(l+r)>>1;
	solve(l,mid);
	solve(mid+1,r);
	sort(y+l,y+mid+1,[](no x,no y){
		return x.b<y.b||x.b==y.b&&x.c<y.c;
	});
	sort(y+mid+1,y+r+1,[](no x,no y){
		return x.b<y.b||x.b==y.b&&x.c<y.c;
	});
	int i=l,j=mid+1;
	while(j<=r) 
	{
		while(i<=mid&&y[i].b<=y[j].b) 
		{
			add(y[i].c,y[i].d);
			i++;
	    }
	    y[j].ans+=ask(y[j].c);
	    j++;
  }
  for(int k=l;k<i;k++) add(y[k].c,-y[k].d);
  return;
}
int ans[maxn];
signed main()
{
	n=read();int k=read();k=0;
	for(int i=1;i<=n;i++)
	{
		x[i].a=read();x[i].b=read();x[i].c=read();
		x[i].d=1;x[i].ans=0;
	}
	sort(x+1,x+n+1);
	for(int i=1;i<=n;i++)
	{
		k++;
		if(x[i]!=x[i+1])
		{
			y[++tot]=x[i];
			y[tot].d=k;
			k=0;
		}
	}
	solve(1,tot); 
	for(int i=1;i<=tot;i++) 
	{
		x[i].ans+=x[i].d-1;
		ans[x[i].ans]+=x[i].d;
	}
	for(int i=1;i<=n;i++)
	{
		cout<<ans[i]<<endl;
	}
	return 0;
}

标签:ch,int,分治,mid,le,CDQ
From: https://www.cnblogs.com/fengyixuan2027/p/18307897

相关文章

  • CDQ 分治学习笔记
    CDQ分治的流程大致是将对于区间\([l,r]\)中点\(x,y\)的计数分为两类处理:\(x,y\)均位于\([l,mid]\)或\([mid+1,r]\)中,这样的点对贡献可以递归解决。\(x,y\)分别位于\([l,mid]\)和\([mid+1,r]\)中,这样的点对通过一些操作来统计贡献。显然这两类贡献之和即为......
  • 点分治学习笔记
    分治就是将一个问题划分成多个子问题来求解。点分治就是在树上进行分治,一般是来统计路径信息的。对于以\(u\)为根的子树内,所有的路径可以划分为两类,一类跨越了子树,经过了\(u\),另一类没有跨越,只经过子树中的点,而子树内的点又可以再分类,这就是点分治。为了保证时间复杂度,每次......
  • 点分治
    点分治及其应用算法:点分治,树的重心。思想先说一下点分治的基本思想:选择树上一个点作为分治中心,为了保证复杂度,选择的点有一些特殊的要求。接下来,把原问题分解成几个相同的子问题,进行递归解决。一般地,我们假设当前根节点为\(rt\),所以我们要统计的路径必然满足以下二者之一:......
  • 分治
    快速排序每次找一个基准值,比基准值小的放左边,比基准值大的放右边,在分别对左右排序标准代码:voidwork(intl,intr){ if(l>=r)return; intmid=(l+r)/2; mid=a[mid]; intx=l,y=r; while(x<=y) { while(a[x]<mid)x++; while(a[y]>mid)y--; ......
  • 线段树分治学习笔记
    线段树分治是一种通过线段树维护时间轴,实现一些可撤销的信息维护问题的手段。线段树分治是离线算法。具体地,对于若干个修改与询问,按照时间戳像区间修改一样挂在线段树的节点上,然后遍历整棵线段树,将节点上的操作计入贡献,对于一个时间戳为\(t\)的询问,线段树上区间\([t,t]\)即......
  • cdq分治
    cdq分治其大致形式为递归左右半边考虑左半边对右半边的影响二维偏序(归并排序):计算数对\((i,j)\)满足\(a_i<a_j\)并且\(b_i<b_j\)的数对数量先以\(a_i\)排序,这样条件被转换为\(i<j\)且\(b_i<b_j\)考虑cdq分治先递归左右半边对左右两边各开一个指针,若\(b_i<b_......
  • Day 2 - 分治与倍增
    分治的延伸应用应用场景优化合并假设将两个规模\(\frac{n}{2}\)的信息合并为\(n\)的时间复杂度为\(f(n)\),用主定理分析时间复杂度\(T(n)=2\timesT(\frac{n}{2})+f(n)\)。能直观的看出优化程度还是很大的。归并排序中\(f(n)=O(n)\),则\(T(n)=O(n\logn)\)。......
  • 递归 | 分治
    这两个算法有部分重合,所以一起讲。递归\(\sf\small\color{gray}Recursion\)递归是递归函数的灵活运用。说到底,它是一个\(\color{blue}\texttt{C++}\)的一个语言特性。在函数内部调用函数,会使得思路更加清晰明了。观察生活,很多事情随着规模或阶段的上升而变得越来越复杂。......
  • 读人工智能全传03分治策略
    1. 黄金年代1.1. 图灵在他发表的论文《计算机器与智能》中介绍了图灵测试,为人工智能学科迈出第一步做出了重大贡献1.2. 美国在第二次世界大战后几十年里计算机技术发展的特色,也是美国在未来60年内确立人工智能领域国际领先地位的核心1.3. 1955年,麦卡锡向洛克菲勒研究所撰......
  • CF1039D You Are Given a Tree (树形 dp + 贪心 + 根号分治)
    CF1039DYouAreGivenaTree树形dp+贪心+根号分治题目是一个经典问题,可以用树形dp和贪心解决。设\(f_u\)表示以\(u\)节点为端点能够剩下的最长路径。考虑从叶子节点往上合并贪心,那么如果能够合并出包含\(u\)节点的大于等于\(k\)的路径,那么就合并,\(f_u=0\);否......