首先按照
a
a
a排序。在分治的时候就没有第一维的影响了。
然后分治的时候两个区间分别按照第二维排序。记左右两个指针。
右边指针一个一个向右跳,左边指针移到第二维小等于右边指针的最大位置。并且在左指针跳的时候把对应的
c
c
c记入权值树状数组里面。左指针跳完之后用右边的
c
c
c查一查前缀和即可。
分治完之后要把影响消掉。
树状数组要开权值大小。
并且要去重。因为相同的之间是相互大于。
#include<bits/stdc++.h>
#define re register
#define cs const
#define lowbit(x) (x&(-x))
#define mid (l+r>>1)
cs int N=1e5+10,K=2e5+10;
int n,k,tot,tr[K],ans[N];
namespace IO{
cs int Rlen=1<<22|1;
char buf[Rlen],*p1,*p2;
inline char gc(){return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;}
template<typename T>
inline T get(){
char ch;T x;
while(!isdigit(ch=gc()));x=ch^48;
while(isdigit(ch=gc())) x=((x+(x<<2))<<1)+(ch^48);
return x;
}
inline int gi(){return get<int>();}
}
using IO::gi;
struct node{int a,b,c,cnt,num;}a[N];
inline bool comp_all(cs node &a,cs node &b){
if(a.a!=b.a) return a.a<b.a;
if(a.b!=b.b) return a.b<b.b;
return a.c<b.c;
}
inline bool comp_b(cs node &a,cs node &b){
if(a.b!=b.b) return a.b<b.b;
return a.c<b.c;
}
inline bool operator!=(cs node &a,cs node &b){
return (a.a!=b.a)||(a.b!=b.b)||(a.c!=b.c);
}
inline void add(int x,int v){for(;x<=k;x+=lowbit(x)) tr[x]+=v;}
inline int query(int x,int ret=0){for(;x;x-=lowbit(x)) ret+=tr[x];return ret;}
void solve(int l,int r){
if(l==r) return;
solve(l,mid),solve(mid+1,r);
std::sort(a+l,a+mid+1,comp_b);
std::sort(a+mid+1,a+r+1,comp_b);
int pl=l;
for(int pr=mid+1;pr<=r;++pr){
while(pl<=mid&&a[pl].b<=a[pr].b)
add(a[pl].c,a[pl].cnt),++pl;
a[pr].num+=query(a[pr].c);
}while(--pl>=l) add(a[pl].c,-a[pl].cnt);
}
int main(){
// freopen("3131.in","r",stdin);
n=gi(),k=gi();
for(int re i=1;i<=n;++i) a[i].a=gi(),a[i].b=gi(),a[i].c=gi();
std::sort(a+1,a+n+1,comp_all);
for(int re i=1,t=1;i<=n;++i,++t)
if(a[i]!=a[i+1]) a[++tot]=a[i],a[tot].cnt=t,t=0;
solve(1,tot);
for(int re i=1;i<=tot;++i) ans[a[i].num+a[i].cnt-1]+=a[i].cnt;
for(int re i=0;i< n;++i) printf("%d\n",ans[i]);
}