三维偏序
有个元素,每个元素有三个属性值,求满足且且的点对的数目。
cdq分治
一维排序,二维分治,三维BIT。
首先对数列以分别为第一、第二、第三关键字排序,然后对得到的数列进行cdq分治。
设现在处理的区间为,中间点。因为已经按照排过序,所以现在只需要查询有多少元素满足(其中)。对左右两个区间以分别为第一、第二关键字排序。满足左右区间内严格不递降。定义两个指针,分别指向左区间和右区间。对左右区间进行扫描,不断把后移。如果此时有就可以直接用前面出现过的所有元素(满足)维护答案。考虑用树状数组优化,时间复杂度。
例题
题解
要求花的等级。即求所有满足的花的数目。就是一个裸的三维偏序。
去重
假设有个元素三个属性值相等,每个花的等级等于三维均小于它的花的数目加上,但是在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;
}
}