https://www.lydsy.com/JudgeOnline/problem.php?id=3262
三维偏序问题,cdq分治和树套树都能解决,所以还是cdq分治比较爽
我们先按照x排序,然后cdq分治过程中,按照y的关键字归并排序,而且只有左半部分的z加入树状数组统计,右半部分归并时询问树状数组中小于等于它的z有多少个点,就可以加入当前点的答案里了。
也就是每次归并排序,都只计算左区间中的点对右区间的贡献。
#include<bits/stdc++.h>
#define maxl 200010
using namespace std;
int n,k,nn;
struct node
{
int x,y,z;
int cnt,id;
friend bool operator < (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;
}
friend bool operator == (const node &a,const node &b)
{
return a.x==b.x && a.y==b.y && a.z==b.z;
}
}a[maxl],t[maxl];
int b[maxl],cnt[maxl],ans[maxl];
inline void prework()
{
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++)
scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].z);
sort(a+1,a+1+n);
nn=0;
for(int i=1;i<=n;i++)
if(a[i]==a[i-1])
a[nn].cnt++;
else
a[++nn]=a[i],a[nn].cnt=1,a[nn].id=nn;
}
inline void add(int i,int x)
{
while(i<=k)
{
b[i]+=x;
i+=i&-i;
}
}
inline int sum(int i)
{
int ret=0;
while(i)
{
ret+=b[i];
i-=i&-i;
}
return ret;
}
inline void cdq(int l,int r)
{
if(l==r)
return;
int mid=(l+r)>>1;
cdq(l,mid);
cdq(mid+1,r);
int i=l,j=mid+1,k=l;
while(i<=mid && j<=r)
{
if(a[i].y<=a[j].y)
{
add(a[i].z,a[i].cnt);
t[k++]=a[i++];
}
else
{
ans[a[j].id]+=sum(a[j].z);
t[k++]=a[j++];
}
}
while(i<=mid)
{
add(a[i].z,a[i].cnt);
t[k++]=a[i++];
}
while(j<=r)
{
ans[a[j].id]+=sum(a[j].z);
t[k++]=a[j++];
}
for(int i=l;i<=mid;i++)
add(a[i].z,-a[i].cnt);
for(int i=l;i<=r;i++)
a[i]=t[i];
}
inline void mainwork()
{
cdq(1,nn);
for(int i=1;i<=nn;i++)
{
ans[a[i].id]+=a[i].cnt-1;
cnt[ans[a[i].id]]+=a[i].cnt;
}
}
inline void print()
{
for(int i=0;i<n;i++)
printf("%d\n",cnt[i]);
}
int main()
{
prework();
mainwork();
print();
return 0;
}