【CDQ分治】三维偏序

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/qq_42725189/article/details/102586041
  • 题目描述:每个点有三个属性a,b,c。求所有 a i a b i b c i c a_i≤a,b_i≤b,c_i≤c 的点的数量。
  • 排序保证第一维有序,考虑归并排序思想排序第二维。现需处理一个大的子问题,左半边和右半边已经处理好了。现在我们就可以得到两个重要条件。1.左边区块的所有a都小于右边的所有a。因为先前按a排序,且并没有跨过左右区块的分界线交换任意两个数。2.在左右区块内部,b是有序的,在处理完子问题后,靠在子问题内部交换使得b是有序的,即使内部a是无序的。
    在这里插入图片描述
    图不太好画,仅供理解,右区间的b是可以小于左区间的b的
  • 此时就消掉一维,一维有序,然后再树状数组维护一维。
  • 计算左边的点对右边的点的贡献,即用归并的双指针加树状数组维护即可。
  • 代码
#include<bits/stdc++.h>
#define N 200005
using namespace std;
int lbt(int x)
{
	return x&(-x);
}
int nxt[N],lst[N],n,m,cnt,tr[N],ans[N];
struct node{
	int a,b,c,w,f;
}e[N],t[N];
bool cmp(node x,node y)
{
	if(x.a!=y.a)return x.a<y.a;
	if(x.b!=y.b)return x.b<y.b;
	return x.c<y.c;
}
void update(int x,int y)
{
	while(x<=m)
	tr[x]+=y,x=nxt[x];
}
int summ(int x)
{
	int ans=0;
	while(x>0)
	ans+=tr[x],x=lst[x];
	return ans;
}
void cdq(int l,int r)
{
	if(l==r)return;
	int mid=l+r>>1;
	cdq(l,mid);cdq(mid+1,r);
	int p=l,q=mid+1,tot=l-1;
	while(p<=mid&&q<=r)
	{
		if(e[p].b<=e[q].b)
		update(e[p].c,e[p].w),t[++tot]=e[p++];
		else e[q].f+=summ(e[q].c),t[++tot]=e[q++];
	}
	while(p<=mid)update(e[p].c,e[p].w),t[++tot]=e[p++];
	while(q<=r)e[q].f+=summ(e[q].c),t[++tot]=e[q++];
	for(int i=l;i<=mid;i++)
	update(e[i].c,-e[i].w);
	for(int i=l;i<=r;i++)
	e[i]=t[i];
}
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)
	{
		scanf("%d%d%d",&e[i].a,&e[i].b,&e[i].c);
		e[i].w=1;
	}
	for(int i=1;i<=200000;i++)
	nxt[i]=i+lbt(i),lst[i]=i-lbt(i);
	sort(e+1,e+1+n,cmp);
	cnt=1;
	for(int i=2;i<=n;i++)
	{
		if(e[i].a==e[cnt].a&&e[i].b==e[cnt].b&&e[i].c==e[cnt].c)e[cnt].w++;
		else e[++cnt]=e[i];
	}
	cdq(1,cnt);
	for(int i=1;i<=cnt;i++)
	ans[e[i].f+e[i].w-1]+=e[i].w;
	for(int i=0;i<n;i++)
	printf("%d\n",ans[i]);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_42725189/article/details/102586041