【cdq分治】【模板】


Problem

三维偏序的模板题。

Solution:

经典做法是,先排序控制一维,再用cdq分治控制一维,最后用数据结构(通常是树状数组)维护一维。

cdq分治还可以嵌套,每嵌套一次,复杂度乘一个log,可以拓展到n维偏序(但超过3维,效率就很低了,通常会使用kd-tree),因此,这题也可以用两个cdq分治嵌套解决(但我不会)

复杂度O(n log^2 n),cdq分治和树状数组各提供一个log

需要注意的问题:

在cdq分治的外层排序中,必须要满足在cdq内部划分时,右半区间不能对左半区间造成影响,因此,当两个元素完全相同时,需要把他们合成一个元素,最后再累计相同元素的贡献,而且要按照三关键字排序

Code

#include
using namespace std;

inline int read()
{
	register int x=0,w=1;
	register char ch=getchar();
	while((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
	if(ch=='-') {ch=getchar();w=-1;}
	while(ch>='0'&&ch<='9') {x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
	return x*w;
}
inline void write(int x)
{
	if(x<0) {putchar('-');x=~(x-1);}
	if(x>9) write(x/10);
	putchar('0'+x%10);
}
const int N=1e5+100;
int n,k;
struct node{
	int a,b,c,s,ans;
	bool operator<(const node&x)const{
	     if(a!=x.a) return a>1;
	cdq(l,mid);cdq(mid+1,r);
	sort(a+l,a+mid+1,cmp);sort(a+mid+1,a+r+1,cmp);
	int pos=l;
	for(int i=mid+1;i<=r;++i)
	{
		while(pos<=mid&&a[pos].b<=a[i].b) {
			add(a[pos].c,a[pos].s);pos++;
		}
		a[i].ans+=ask(a[i].c);
	}
	for(int i=l;i

cdq分治的递归函数跟归并排序十分类似,实际上,我们可以把cdq中的sort改为归并排序,从而减少常数。

常数优化版本

#include
using namespace std;

inline int read()
{
	register int x=0,w=1;
	register char ch=getchar();
	while((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
	if(ch=='-') {ch=getchar();w=-1;}
	while(ch>='0'&&ch<='9') {x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
	return x*w;
}
inline void write(int x)
{
	if(x<0) {putchar('-');x=~(x-1);}
	if(x>9) write(x/10);
	putchar('0'+x%10);
}
const int N=1e5+100;
int n,k;
struct node{
	int a,b,c,s,ans;
	bool operator<(const node&x)const{
	     if(a!=x.a) return a>1;
	cdq(l,mid);cdq(mid+1,r);
//	sort(a+l,a+mid+1,cmp);sort(a+mid+1,a+r+1,cmp);
	int pos=l;
	for(int i=mid+1;i<=r;++i)
	{
		while(pos<=mid&&a[pos].b<=a[i].b) {
			add(a[pos].c,a[pos].s);pos++;
		}
		a[i].ans+=ask(a[i].c);
	}
	for(int i=l;imid)
		tmp[++pos]=a[q++];
		else if(q>r) tmp[++pos]=a[p++];
		else tmp[++pos]=a[a[p].b