CDQ divide and conquer

Keywords: Algorithm

I'm just learning cdq divide and conquer today. I'd like to write a little summary.

First of all, we can see that this name is related to divide and conquer. The prefix name is actually not so important. After studying all morning, I feel that this thing is added to the merge

Directly look at the most template topic, flowers bloom on the street.

The name is really poetic and rare. I thought there might be a similar expression in question, but goose didn't. It was a mistake after all.

The meaning of the question is very simple. Find the three-dimensional partial order.

According to the method of cdq, the first dimension is sorted directly, which means that only the previous points can be updated to the later points, so we can divide and conquer; A motley crew is as like as two peas. The next element is left and right. The elements are only about the left and right. That is to say, the internal order of the two sub intervals of the left and right intervals is no longer important. Finally, take the weight of the third dimension as the bucket and query the answer with a tree array. Complexity \ (O (nlogn \).

This is the basic idea of cdq, which can reduce the dimension based on partial sorting. Of course, there are some things to pay attention to:

  • Finally, when the tree array is emptied to prepare for the next time, try not to use the traditional memset. It is easy to time out (or it will certainly time out, because memset will make the complexity reach about \ (O(N^2) \). It is especially miserable. It is not as bad as violence.
  • The sequence needs to be analyzed on a case by case basis. The order here refers to whether the right interval is processed first or merged first. This problem is to deal with the right interval first, but for some problems, it must be merged first. For example, the less classical LIS counting problem should merge first and then deal with the right interval. The specific reasons are still under consideration and will be summarized later.

code:

#include<cstdio>
#include<cstring>
#include<algorithm>
#define zczc
using namespace std;
const int N=100010;
const int M=200010;
inline void read(int &wh){
	wh=0;int f=1;char w=getchar();
	while(w<'0'||w>'9'){if(w=='-')f=-1;w=getchar();}
	while(w<='9'&&w>='0'){wh=wh*10+w-'0';w=getchar();}
	wh*=f;return;
}

int m,n,cnt,ans[N];
struct no{
	int x,y,z,num,ans;
}a[N],b[N];//b is the auxiliary array of merge sort 
bool operator ==(no s1,no s2){
	return s1.x==s2.x&&s1.y==s2.y&&s1.z==s2.z;
}
inline bool cmp1(no s1,no s2){
	if(s1.x!=s2.x)return s1.x<s2.x;
	else if(s1.y!=s2.y)return s1.y<s2.y;
	else return s1.z<s2.z;
}

#define lowbit (wh&-wh)
int t[M];
inline void clear(){
	memset(t,0,sizeof(t));
	return;
}
inline void init(int wh){
	for(;wh<=n;wh+=lowbit)t[wh]=0;
}
inline void change(int wh,int val){
	for(;wh<=n;wh+=lowbit)t[wh]+=val;
	return;
}
inline int work(int wh){
	int an=0;
	for(;wh;wh-=lowbit)an+=t[wh];
	return an;
}
#undef lowbit
 
void cdq(int l,int r){
	if(l==r)return;
	int mid=l+r>>1;
	cdq(l,mid);cdq(mid+1,r);
	
	for(int i=l,j=mid+1,k=l;k<=r;k++){
		if(j>r||(i<=mid&&a[i].y<=a[j].y)){
			b[k]=a[i++];
			change(b[k].z,b[k].num);
		}
		else{
			b[k]=a[j++];
			b[k].ans+=work(b[k].z);
		}
	}
	for(int i=l;i<=r;i++){
		a[i]=b[i];
		init(b[i].z);
	}
	
	return;
}

signed main(){
	
	#ifdef zczc
	freopen("in.txt","r",stdin);
	#endif
	
	read(m);read(n);
	for(int i=1;i<=m;i++){
		read(a[i].x);read(a[i].y);read(a[i].z);a[i].num=1;
	}
	sort(a+1,a+m+1,cmp1);
	for(int i=1;i<=m;i++){
		if(cnt!=0&&a[cnt]==a[i])a[cnt].num++;
		else a[++cnt]=a[i];
	}
	
	cdq(1,cnt);
	for(int i=1;i<=cnt;i++)ans[a[i].ans+a[i].num-1]+=a[i].num;
	for(int i=0;i<m;i++)printf("%d\n",ans[i]);
	
	return 0;
}

Double experience: Dynamic reverse order pair , just a little transformation. Too lazy to write.

Posted by RaythMistwalker on Fri, 03 Dec 2021 20:16:00 -0800