\(\color{#0066ff}{题目描述}\)
有 n 个元素,第 i 个元素有 \(a_i 、b_i 、c_i\) 三个属性,设 f(i) 表示满足 \(a_j \leq a_i 且 b_j \leq b_i 且 c_j \leq c_i\) 的 j 的数量。
对于 \(d \in [0, n)\) ,求 \(f(i) = d\) 的数量
\(\color{#0066ff}{输入格式}\)
第一行两个整数 n、 k,分别表示元素数量和最大属性值。
之后 n 行,每行三个整数 \(a_i 、b_i 、c_i\) ,分别表示三个属性值。
\(\color{#0066ff}{输出格式}\)
输出 n 行,第 \(d + 1\) 行表示 \(f(i) = d\) 的 i 的数量。
\(\color{#0066ff}{输入样例}\)
10 3
3 3 3
2 3 3
2 3 1
3 1 1
3 1 2
1 3 1
1 1 2
1 2 2
1 3 2
1 2 1
\(\color{#0066ff}{输出样例}\)
3
1
3
0
1
0
1
0
0
1
\(\color{#0066ff}{数据范围与提示}\)
\(1\leq n \leq 100000,1\leq k\leq 200000\)
\(\color{#0066ff}{题解}\)
cdq 分治
对序列分治,每次统计左区间每个元素对于右区间每个元素的贡献
首先分别以x,y,z为1,2,3关键字排序
那么现在x是有序的
然后对序列cdq分治,每次把左右区间按y,z排序
这样保证了左区间所有x\(\leq\)右区间所有x,且左右区间y有序
维护左区间的指针,for右区间每一个元素,只要左区间指针的元素y比当前小,就在权值树状数组上以z为下标++
然后用当前的z收集ans
注意千万不要单开ans数组收集答案
因为当前元素在每次sort后会变成其他的,这样收集就乱了
最后开桶统计即可
#include<cstdio>
#include<queue>
#include<vector>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cctype>
#include<cmath>
#define _ 0
#define LL long long
#define Space putchar(' ')
#define Enter putchar('\n')
#define fuu(x,y,z) for(int x=(y),x##end=z;x<=x##end;x++)
#define fu(x,y,z) for(int x=(y),x##end=z;x<x##end;x++)
#define fdd(x,y,z) for(int x=(y),x##end=z;x>=x##end;x--)
#define fd(x,y,z) for(int x=(y),x##end=z;x>x##end;x--)
#define mem(x,y) memset(x,y,sizeof(x))
#ifndef olinr
inline char getc()
{
static char buf[100001],*p1=buf,*p2=buf;
return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,100001,stdin),p1==p2)? EOF:*p1++;
}
#else
#define getc() getchar()
#endif
template<typename T>inline void in(T &x)
{
int f=1; char ch; x=0;
while(!isdigit(ch=getc()))(ch=='-')&&(f=-f);
while(isdigit(ch)) x=x*10+(ch^48),ch=getc();
x*=f;
}
int n,k;
struct BIT
{
private:
int c[205050];
#define low(x) ((x)&(-x))
public:
inline void add(int pos,int v) {for(int i=pos;i<=k;i+=low(i)) c[i]+=v;}
inline int query(int pos) {int ans=0; for(int i=pos;i;i-=low(i)) ans+=c[i]; return ans;}
};
struct node
{
int x,y,z;
int num,ans;
friend bool operator != (const node &a,const node &b)
{
return a.x!=b.x||a.y!=b.y||a.z!=b.z;
}
}e[300000];
BIT t;
int ans[300000];
inline void cdq(int l,int r)
{
if(l==r) return ;
int mid=(l+r)>>1;
cdq(l,mid);cdq(mid+1,r);
std::sort(e+l,e+mid+1,[](const node &a,const node &b){return (a.y<b.y)||(a.y==b.y&&a.z<b.z);});
std::sort(e+mid+1,e+r+1,[](const node &a,const node &b){return (a.y<b.y)||(a.y==b.y&&a.z<b.z);});
int now=l;
fuu(i,mid+1,r)
{
//每一个都会产生贡献,所以是num
while(now<=mid&&e[now].y<=e[i].y) t.add(e[now].z,e[now].num),now++;
e[i].ans+=t.query(e[i].z);
}
fuu(i,l,now-1) t.add(e[i].z,-e[i].num);
}
int main()
{
in(n),in(k);
fuu(i,1,n) in(e[i].x),in(e[i].y),in(e[i].z),e[i].num=1;
std::sort(e+1,e+n+1,[](const node &a,const node &b){return (a.x<b.x)||(a.x==b.x&&(a.y<b.y))||(a.x==b.x&&a.y==b.y&&a.z<b.z);});
int tmp=0;
//去重,相同的压一起(跟自己相同的算在ans的一部分,但自己对自己无贡献)
fuu(i,1,n)
{
if(e[i]!=e[i-1]) e[++tmp]=e[i];
else e[tmp].num++;
}
cdq(1,tmp);
//ans里面套的是当前元素的答案(要加上相同的-自己)
//外面因为跟自己相同的都是成立的,所以+=个数
fuu(i,1,tmp) ans[e[i].ans+e[i].num-1]+=e[i].num;
fuu(i,0,n-1) printf("%d\n",ans[i]);
return ~~(0^_^0);
}