三维偏序(cdq分治)

1,953 阅读2分钟

三维偏序

nn个元素,每个元素pip_i有三个属性值ai,bi,cia_i,b_i,c_i,求满足aiaja_i\leq a_jbibjb_i\leq b_jcicjc_i\leq c_j的点对(i,j)(i,j)的数目。

cdq分治

一维排序,二维分治,三维BIT。
首先对数列以ai,bi,cia_i,b_i,c_i分别为第一、第二、第三关键字排序,然后对得到的数列进行cdq分治。
设现在处理的区间为(l,r)(l,r),中间点mid=(l+r)>>1mid=(l+r)>>1。因为已经按照aia_i排过序,所以现在只需要查询有多少元素满足bibj  cicjb_i\leq b_j\ \ c_i\leq c_j(其中i[l,mid],j[mid+1,r]i\in[l,mid],j\in[mid+1,r])。对左右两个区间以bi,cib_i,c_i分别为第一、第二关键字排序。满足左右区间内bib_i严格不递降。定义两个指针i,ji,j,分别指向左区间和右区间。对左右区间进行扫描,不断把i,ji,j后移。如果此时有bibjb_i\leq b_j就可以直接用前面出现过的所有元素kkkk满足ckcjc_k\leq c_j)维护答案。考虑用树状数组优化,时间复杂度O(nlog2n)O(nlog^2n)

例题

bzoj 陌上花开

题解

要求花jj的等级。即求所有满足aiaj,bibj,cicja_i\leq a_j,b_i\leq b_j,c_i\leq c_j的花的数目。就是一个裸的三维偏序。

去重

假设有ww个元素三个属性值相等,每个花的等级等于三维均小于它的花的数目加上w1w-1,但是在cdq分治过程中只计算了左边元素对右边的贡献,这就导致相同的花等级不同。所以可以在进行分治前预处理出所有相同的花,记录下它们的数目,看做一朵花。用BIT维护答案时直接加入花的总数即可。

代码

#include<bits/stdc++.h>
#define rep(i,st,ed) for(int i=st;i<=ed;++i)
#define lb x&-x
using namespace std;
const int N=1E5+10;
struct Node{
	int x,y,z,num,ans;
}p[N],a[N];
int n,k,tot;
int anss[2*N],t[2*N];
inline int read()
{
	int ret=0,flag=1;char ch=getchar();
	while(ch<'0' || ch>'9')
	{
		if(ch=='-')flag=-1;
		ch=getchar();
	}
	while(ch>='0' && ch<='9')ret=ret*10+ch-'0',ch=getchar();
	return ret*flag;
}
bool cmpx(const Node &a,const Node &b)
{
	if(a.x==b.x)
	{
		if(a.y==b.y)
			return a.z<b.z;
		return a.y<b.y;
	}
	return a.x<b.x;
}
bool cmpy(const Node &a,const Node &b)
{
	if(a.y==b.y)
		return a.z<b.z;
	return a.y<b.y;
}
void add(int x,int val)
{
	while(x<=k)
	{
		t[x]+=val;
		x+=lb;
	}
}
int query(int x)
{
	int ret=0;
	while(x)
	{
		ret+=t[x];
		x-=lb;
	}
	return ret;
}
void solve(int l,int r)
{
	if(l==r)
		return;
	int mid=(l+r)>>1;
	solve(l,mid);solve(mid+1,r);
	sort(a+l,a+mid+1,cmpy);
	sort(a+mid+1,a+r+1,cmpy);
	int ind=l;
	rep(i,mid+1,r)
	{
		while(ind<=mid && a[ind].y<=a[i].y)
		{
			add(a[ind].z,a[ind].num);
			++ind;
		}
		a[i].ans+=query(a[i].z);
	}
	rep(i,l,ind-1)
		add(a[i].z,-a[i].num);
}
void print()
{
	rep(i,1,tot)
	{
		printf("%d %d %d %d\n",a[i].x,a[i].y,a[i].z,a[i].num);
	}
}
int main()
{
	n=read();k=read();
	rep(i,1,n)
	{
		p[i].x=read();p[i].y=read();p[i].z=read();
	}
	sort(p+1,p+n+1,cmpx);
	Node now=p[1];
	int cnt=0;
	rep(i,1,n) 
	{
		++cnt;
		Node nxt=p[i+1];
		if(nxt.x!=now.x || nxt.y!=now.y || nxt.z!=now.z)
		{
			a[++tot]=now;
			a[tot].num=cnt;
			cnt=0;
		}
		now=nxt;
	}
//	print();
	solve(1,tot);
	rep(i,1,tot)
	{
		anss[a[i].ans+a[i].num-1]+=a[i].num;
	}
	rep(i,0,n-1)
	{
		cout<<anss[i]<<endl;
	}
}